阅读量: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就不会自动解包
- 为什么 Bill 渲染出来有双引号?
- 为什么 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>
答案:
- 因为使用的是 shallowReactive,shallowReactive 内部的 ref 是不会自动解包的
- 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)
- 数组和集合里面使用 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>
- 在模板中的自动解包
在模板里面,只有顶级的 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-