vue3 响应式 Ref和Reactive 中的一些细节

avatar
作者
猴君
阅读量:0

vue3 响应式API Ref和Reactive 中的一些细节

shallowRef(浅响应式)

const state= shallowRef({count:1}); state.value.count+=2; // 不会触发页面的响应式更新 state.value={count:2} // 才会触发更新 

数据更新

响应式数据的更新*(响应式数据更新是同步的),带来了DOM的自动更新,但是DOM的更新不是同步的*,这意味这当响应式数据发生改变之后,我们去获取DOM值,拿到的还是之前的DOM值

如果要获取更新过的DOM数据要使用nextTick

shallowReactive(第一层的数据改变会触发更新)

和上面shallowRef作用一样

const state= shallowReactive({count1:1,nested:{count2:2}}); state.count1+=2; // 会触发页面的更新 state.nested.count2+=2 // 不会触发更新 

reactive的局限性

当对reactive对象进行解构时会丢失响应式

在部分函数传参中会丢失响应式

let testReactive = reactive({ 	count: 1, }); onMounted(() => { 	setTimeout(() => { 		func(testReactive); // 传递一整个reactive对象响应式不会丢失         func(testReactive.count); // 传递reactive对象的某个值响应式丢失 	}, 1000); });  const func = (state) => { 	state.count ? (state.count = 4) : (state = 2); }; 

ref解包细节

const name1 = ref(1); const name2 = reactive({ name1 }); console.log("n1", name1.value); // 1 console.log("n2", name2); // 1 自动解包 (解构出name1再加.value) 

如果时shallowReactive就不会自动解包

  1. 为什么 Bill 渲染出来有双引号?
  2. 为什么 2 秒后界面没有渲染 Smith ?
<template>   <div>{{ obj.name }}</div> </template>  <script setup> import { ref, shallowReactive } from 'vue' const name = ref('Bill') // name 是一个 ref 值 const obj = shallowReactive({   name }) setTimeout(() => {   obj.name = 'John' }, 1000) setTimeout(() => {   name.value = 'Smith' }, 2000) </script>  <style lang="scss" scoped></style> 

答案:

  1. 因为使用的是 shallowReactive,shallowReactive 内部的 ref 是不会自动解包的
  2. 1秒后,obj.name 被赋予了 John 这个字符串值,这就使得和原来的 ref 数据失去了联系

如果想要渲染出 Smith,修改如下:

import { ref, shallowReactive } from 'vue' const name = ref('Bill') // name 是一个 ref 值 const obj = shallowReactive({   name }) setTimeout(() => {   name.value = 'John' }, 1000) setTimeout(() => {   name.value = 'Smith' }, 2000) 
  1. 数组和集合里面使用 ref

如果将 ref 数据作为 reactvie 数组或者集合的一个元素,此时是不会自动解包的

// 下面这些是官方所给的例子 const books = reactive([ref('Vue 3 Guide')]) // 这里需要 .value console.log(books[0].value)  const map = reactive(new Map([['count', ref(0)]])) // 这里需要 .value console.log(map.get('count').value) 
<template>   <div></div> </template>  <script setup> import { ref, reactive } from 'vue' const name = ref('Bill') const score = ref(100) const state = reactive({   name,   scores: [score] }) console.log(state.name) // 会自动解包 console.log(state.scores[0]) // 不会自动解包 console.log(state.scores[0].value) // 100 </script>  <style lang="scss" scoped></style> 
  1. 在模板中的自动解包

在模板里面,只有顶级的 ref 才会自动解包。

<template>   <div>     <div>{{ count }}</div>     <div>{{ object.id }}</div>   </div> </template>  <script setup> import { ref } from 'vue' const count = ref(0) // 顶级的 Ref 自动解包 const object = {   id: ref(1) // 这就是一个非顶级 Ref 不会自动解包 } </script>  <style lang="scss" scoped></style> 

上面的例子,感觉非顶级的 Ref 好像也能够正常渲染出来,仿佛也是自动解包了的。

但是实际情况并非如此。

<template>   <div>     <div>{{ count + 1 }}</div>     <div>{{ object.id + 1 }}</div>   </div> </template>  <script setup> import { ref } from 'vue' const count = ref(0) // 顶级的 Ref 自动解包 const object = {   id: ref(1) // 这就是一个非顶级 Ref 不会自动解包 } </script>  <style lang="scss" scoped></style> 

例如我们在模板中各自加 1 就会发现上面因为已经解包出来了,所以能够正常的进行表达式的计算。

但是下面因为没有解包,意味着 object.id 仍然是一个对象,因此最终计算的结果为 [object Object]1

因此要访问 object.id 的值,没有自动解包我们就手动访问一下 value

<template>   <div>     <div>{{ count + 1 }}</div>     <div>{{ object.id.value + 1 }}</div>   </div> </template> 

-EOF-

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!