我们先来观察一下这段代码
<script setup>
import { shallowReactive } from 'vue'
const test = shallowReactive({
count1: 0,
nestedObj: {
count2: 0
}
})
const add = () => {
test.count1++
test.nestedObj.count2++
}
</script>
<template>
<button @click="add">+</button>
<div>{{ test.count1 }}</div>
<div>{{ test.nestedObj.count2 }}</div>
</template>
你觉得当点击+号时,视图会如何更新?我给三个选项吧:
选项A:count1更新,count2不变
选项B:count1更新,count2改变但视图不更新
选项C:count1更新,count2改变且视图更新
我相信很大一部分人会选择A或者B,但当你亲自调试时发现,自己的直觉错误了。
我们先来回顾一下什么是shallowReactive,查阅一下官方文档,是这样描述的:
和 reactive()
不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
对照上面代码来理解,nestedObj这个嵌套的对象,不再具备响应式,但为什么视图却更新了呢?
这里我提示一下,add这个方法的effect是什么?显然是Vue内部追踪到了test.count1的依赖,然后count1是第一层具备响应式的数据,Vue自然会触发更新。但是我们不要忽略一个点,此时count2其实数据发生改变了,而Vue的diff此时并不关心到底谁是reactive响应式对象,换言之,这次effect是数据驱动了视图发生了变化,而test.count1就是带动count2产生视图变化的“罪魁祸首”。
我们平时不能机械地将shallowReactive理解为一个不具备响应式的死数据,对于Vue来说,只是对于深层次的这些对象不再进行及时的追踪,但不代表不能手动更新或者由于副作用的影响而造成视图的变化。
diff是一个高明的算法,保证了视图更新的效率,且不至于消耗更多额外资源。