目录
3.1 为何需要 ref ,不能用 reactive 替代?
扫描二维码关注公众号,回复:
14887622 查看本文章
1. 是什么?
1.1 ref
- 生成值类型的响应式数据
- 可用于模板 { { }} 和 reactive(在模板和 reactive 中不用.value)
- 通过 .value 修改值
- 命名规范xxxRef,便于理解
- 可获取 DOM 节点
<template>
<div>
<p>ref demo</p>
{
{ ageRef }}
{
{ state.name }}
</div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
name: 'RefDemo',
setup() {
const ageRef = ref(20) // 值类型响应式,命名xxxRef
const nameRef = ref('张三')
const state = reactive({
name: nameRef
})
setTimeout(() => {
console.log('ageRef', ageRef.value)
ageRef.value = 25 // .value 修改值
nameRef.value = '李四'
}, 1500)
return {
ageRef,
state
}
}
}
</script>
获取 DOM 节点
<template>
<p ref="elemRef">我是一行文字</p>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const elemRef = ref(null)
onMounted(() => {
console.log(elemRef.value, elemRef.value.innerHTML)
})
return {
elemRef
}
}
}
</script>
1.2 toRef
- 针对一个响应式对象( reactive 封装)的prop(属性)
- 用于普通对象,产出的结果不具备响应式
- 创建一个 ref ,具有响应式
- 两者保持引用关系(同步更改)
<template>
<p>toRef demo</p>
{
{ ageRef }} -- {
{ state.name }} -- {
{ state.age }}
</template>
<script>
import { toRef, reactive } from 'vue'
export default {
setup() {
const state = reactive({
name: '张三',
age: 20
})
// toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
/* const state = {
name: '张三',
age: 20
} */
const ageRef = toRef(state, 'age')
setTimeout(() => {
state.age = 25 // ageRef 和 state.age 同时改变
}, 1500)
setTimeout(() => {
ageRef.value = 30 // ageRef 和 state.age 同时改变
}, 3000)
return {
state,
ageRef
}
}
}
</script>
1.3 toRefs
- 将响应式对象( reactive 封装)转换为普通对象
- 对象的每个 prop(属性)都是对应的 ref
- 两者保持引用关系
- 合成函数返回响应式对象
<template>
<p>toRefs demo</p>
{
{ age }}--{
{ name }}
</template>
<script>
import { toRefs, reactive } from 'vue'
export default {
setup() {
const state = reactive({
name: '张三',
age: 20
})
// 将响应式对象,变成普通对象
const stateAsRefs = toRefs(state)
// 每个属性都是 ref 对象
/* const { age: ageRef, name: nameRef } = stateAsRefs
return {
ageRef,
nameRef
} */
// 返回之后,页面模板中可直接使用 name,age 属性,不需在 stateAsRefs.name ,且具有响应式
return stateAsRefs //不能返回 { stateAsRefs }
}
}
</script>
合成函数返回响应式对象
2. 最佳使用方式
- 用 reactive 做对象的响应式,用 ref 做值类型的响应式
- setup 中返回 toRefs(state),或者 toRef(state, 'xxx')
- ref 变量命名 xxxRef
- 合成函数返回响应式对象时,使用 toRefs,有助于使用方对数据进行结构
3. 进阶,深入理解
3.1 为何需要 ref ,不能用 reactive 替代?
- 返回普通值类型,会丢失响应式
- 如在 setup,computed,合成函数,都有可能返回值类型
- Vue 如果不定义 ref ,用户将自造 ref ,反而混乱
<template>
<p>why ref</p>
{
{ x }}--{
{ y }}--{
{ age }}---{
{ ageRef }}---{
{ name1 }}
</template>
<script>
import { ref, toRefs, reactive, computed } from 'vue'
function useFeatureX() {
const state = reactive({
x: 1,
y: 2
})
return toRefs(state)
}
export default {
setup() {
const { x, y } = useFeatureX()
let age = 20 // 不具备响应式
let ageRef = ref(20) // 具备响应式
const state = reactive({
name: '张三'
})
const name1 = computed(() => {
return state.name + '啦啦啦' // 返回类似 ref 的对象,也有 .value
})
console.log('name1.value', name1.value)
setTimeout(() => {
age = 25 // 页面值不会改变
ageRef.value = 25 // 页面值会改变
}, 1000)
return {
x,
y,
age,
ageRef,
name1
}
}
}
</script>
3.2 为何需要 .value ?
- ref 是一个对象(不丢失响应式),value 存储值
- 通过 .value 属性的 get 和 set 实现响应式
- 用于模板{ { }},reactive 时,不需要 .value ,其他情况都需要 .value
/* 定义成普通值类型,丢失响应式 */
function computed1(getter) {
let value = 0 // 定义成普通值类型,丢失响应式
setTimeout(() => {
value = getter()
}, 1500)
return value
}
let a = 5
a = computed1(() => 1000)
// 1.5,秒之后再打印 a 的值,依然是 0,而没有变成 1000
/* 相当于
let a = 5
let b = a
a = 6
console.log(b) // 5
*/
setTimeout(() => {
console.log(a) // 0
}, 2000)
/* 定义成 ref 类型,通过value,保留响应式 */
function computed2(getter) {
let ref = {
value: null // 定义成 ref 类型,通过value,保留响应式
}
setTimeout(() => {
ref.value = getter()
}, 1500)
return ref
}
let c = {}
c = computed2(() => 1000)
// 1.5,秒之后再打印 c 的值,变成 {value: 1000}
/* 相当于
let a = { value: 5}
let b = a
a.value = 6
console.log(b.value) // 6
*/
setTimeout(() => {
console.log(c) // {value: 1000}
}, 2000)
3.3 为何需要 toRef toRefs ?
- 初衷:在不丢失响应式的情况下,把对象数据 分解/扩散(解构)
- 前提:针对的是响应式对象(reactive 封装的),而非普通对象
- 注意:不创造响应式,而是延续响应式