经过之前两篇 ref函数 和 reactive函数 我们对于这两种转换响应式数据方法的底层原理都有了一定的了解,因此,我们可以总结一下这两种方式的区别
定义数据角度
从原理角度
从使用角度
通过ref函数转化后,会返回一个 RefImpl 引用对象。在js中使用该变量时,我们需要通过 xxx.value 来进行操作。但是当我们在模板中通过插值语法来使用xxx变量时,Vue3 会自动解包,找到 xxx.value ,而我们在模版上只需要按照正常插值语法书写即可
工作:{{ name }}
let name = ref('al')
function changeName() {
name.value = '汤圆仔'
}
而经过 reactive 函数转化的普通对象中,因为返回的是 Proxy 代理对象,所以不需要解包这一操作,就和普通对象一样操作即可
工作:{{ userInfo.name }}
let userInfo = reactive({
name:'al'
});
function change() {
userInfo.name = 999
}
如果 reactive 函数接收的是一个 通过ref函数转化的响应式对象,那么这个经过ref转化的响应式数据在作为 reactive 函数再次转化之后的响应式数据在被访问或被修改时,也会像一个普通属性一样,完成自动解包的操作
let userInfo = ref({name:'al'}); //这里返回的是一个 RefImpl 引用对象
const state = reactive({
userInfo //这里的userInfo 就是上面的 RefImpl 引用对象
})
console.log(state) //返回的是Proxy对象 Proxy(Object){userInfo:RefImpl}
console.log(state.userInfo.name) // al 当通过 reactive 返回的 Proxy 实例访问name属性时,会自动解包 其实访问的是 state.userInfo.name.value
state.userInfo.name = '汤圆仔' // 同理,修改name值时,也是自动解包,修改的是state.userInfo.name.value = '汤圆仔’
console.log(userInfo.value.name) // 汤圆仔 而且因为 ref是响应式,reactive 也是响应式,所以修改 reactive 对象的数据后其实也同步到了 ref 对象上
如果 reactive 函数接受得是一个标准的数组,那我们在操作数组的时候,也是像操作普通数组一样,通过数组下标进行操作,不用解包
const hobby = reactive(['吃饭']) // 传入标准数组格式
console.log(hobby,'hobby'); // 返回的是一个 Proxy 代理对象 Proxy(Array){0:'吃饭'}
如果 reactive 函数接收的是一个经过ref函数转化过的 数组,那么我们在操作数组的时候,又需要加上 value了,这里就和 上面的 reactive 函数 接收一个 通过ref转化标准对象形式的结果形成了对比
const books = reactive([ref('学习')])
console.log(books,'books') // 返回 Proxy 代理对象 Proxy(Array){0:RefImpl}
console.log(books[0],'books[0]') //直接返回 RefImpl 引用对象
console.log(books[0].value,'books[0].value') //到了这一步,才能真正取到数组第一个值
同理,如果reactive 函数接收的是一个 原生集合类型 (如 Map
) ,也需要 加上 value
const work = reactive(new Map([['前端', ref('学习')]]))
console.log(work, 'work'); // 返回 Proxy 代理对象 Proxy(Map){'前端' => RefImpl}
console.log(work.get('前端'), 'work.get'); // 得到 Proxy 代理对象 Proxy(RefImpl)
console.log(work.get('前端').value, 'work.get.value'); // 得到 键 对应的值 学习
在模板中,只有顶级属性才会被解包,例如下面的 count和object都是顶级属性,但是 object.id则不是顶级属性
let count = ref(0)
let object = { id: ref(1) }
所以在模板插值表达式中,
{{ count + 1 }}
// 渲染正确count 会自动解包 最终展示为 2
{{ object.id + 1 }}
// 渲染错误 最终展示为 [object Object]1 字符串。因为 object是一个对象{id:refImpl} ,object.id 则是一个 RefImpl 引用对象,使用object.id 时,因为 object.id bus不是顶层属性,所以不会自动解包,需要使用 value 属性获取真正的值
为了解决这个问题,我们可以将 object 进行解构,使得object.id 成为顶层属性,从而实现自动解包
let object = { id: ref(1) }
let {id} = object //通过解构实现,将id变成顶级属性,从而自动解包
{{ id + 1 }}
// 展示正确 最终渲染结果为2