深层次响应式
reactive
和 ref
创建的对象都是深层次的,对象的根属性和嵌套属性都是响应式的。
深层次转换是递归地转为响应式,对象里的每个属性访问都将触发代理的依赖追踪,这种性能负担通常这只有在处理超大型数组或层级很深的对象时才比较明显。例如,一次渲染需要访问 100000+ 个属性时,才会变得比较明显。因此,shallowRef
或 shalloReactive
只在少数特定的场景才会使用。
const state = reactive({ foo: { bar: 1 }, foobar: 2});
浅层次响应式shallowReactive
shallowReactive
创建的响应式对象只在其根属性是响应式的,对所有深层的对象不会做任何的递归深层转换处理,否则和 reactive 没区别。
浅层响应式对深层级属性的访问会很快,但代价是,所有深层的对象视为非响应式的。
const state = shallowReactive({ foo: { bar: 1 }, foobar: 2});
上图中,state.foo.bar
没有发生改变,只有在其他响应式更新之后,才间接地更新它的状态。
替换顶级对象失去响应式
对于 reactive
和 shallowReactive
,如果替换了顶级对象就会失去响应式:
let state = shallowReactive({ foo: { bar: 1 }, foobar: 2});function changeState() { state = { foo: { bar: 1000 }, foobar: 2000 };}
如上图所示,state.foobar
在替换顶级对象之前,一直是有响应式的,但是被替换了之后,就失去了响应式。
需要思考的是,为什么替换了顶级对就失去了响应式,请先阅读 Vue 中的响应性是如何工作的,官方文档给了一个 reactive 和 ref 创建响应式的伪代码,不难看出,reactive 返回了一个 Proxy。在 getter/setter
上加上 Vue 的追踪代码,实现响应式。
一旦这个 Proxy 对象被替换了,就没有了 getter/setter
,即失去了响应式。
shallowRef
与 shallowReactive
不同的是,shallowRef
创建的响应式对象都是浅层的,并不是说它不是响应式,因为有 triggerRef
API 存在,即主动地通知更新。
let state = shallowRef({ foo: { bar: 1 }, foobar: 2});function changeState() { state.value = { foo: { bar: 1000 }, foobar: 2000 };}
在替换顶层对象之前,点击按钮,state.foo.bar
和 state.foobar
都没有更新。强制通知浅层 ref 更新,可以使用 triggerRef
,具体查看官方文档:triggerRef。
替换顶级对象不会失去响应式
shallowRef
和 ref
创建的响应式对象被替换了之后,依旧保留响应式。
let state = shallowRef({ foo: { bar: 1 }, foobar: 2});function changeState() { // 自加不会触发页面更新 state.value.foo.bar++; state.value.foobar++; // 替换 state.value 的顶级对象页面才会更新 const replace = { foo: { bar: state.value.foo.bar }, foobar: state.value.foobar }; state.value = replace;}
最好的做法是通过 triggerRef
,改造一下代码:
function triggerUpdate() { state.value.foo.bar++; state.value.foobar++; triggerRef(state);}
总结 shallowRef
shallowReactive
没有强制更新的 API,只有第一层属性是响应式的,且嵌套对象属性是间接触发更新的机制。
所以,想要一个浅层的响应式对象,建议使用 shallowRef
,它有 triggerRef
这样的 API,使用起来灵活。
什么时候用到浅层响应式
- 在处理大量数据,需要提高性能(优化性能)时;
- 不希望立马更新,需要自定义更新时机时。