ref函数与reactive函数的对比

 经过之前两篇 ref函数 和 reactive函数 我们对于这两种转换响应式数据方法的底层原理都有了一定的了解,因此,我们可以总结一下这两种方式的区别

 定义数据角度

  • ref既可以用来定义基本数据类型,也可以用来定义对象( 或数组 )类型数据。当 ref 定义对象基本类型数据时,内部其实是自动通过 reactive 函数 转化为代理对象
  • reactive用来定义对象( 或数组 )类型数据。

从原理角度

  • ref 函数通过 Object.definePropetry() 的 get 和 set 来实现响应式( 数据劫持 )
  • reactive 函数则是通过 Proxy 来实现响应式( 数据劫持 ),并通过 Reflect 操作源对象数据。

从使用角度 

通过ref函数转化后,会返回一个 RefImpl 引用对象。在js中使用该变量时,我们需要通过 xxx.value 来进行操作。但是当我们在模板中通过插值语法来使用xxx变量时,Vue3 会自动解包,找到 xxx.value ,而我们在模版上只需要按照正常插值语法书写即可



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

你可能感兴趣的:(vue3专栏,javascript,前端,开发语言)