前言
介绍 reactive 与 shallowReactive 的区别,即深响应和浅响应的区别。
浅响应式与深相应式
const obj = reactive({ foo: { bar: 1 } })
effect(() =>{
console.log(obj.foo.bar)
})
// 修改 obj.foo.bar 的值,并不能触发响应
obj.foo.bar = 2
首先,创建 obj 代理对象,该对象的 foo 属性值也是一个对象,即 { bar: 1} 。接着,在副作用函数内访问 obj.foo.bar 的值。但是我们发现,后续对 obj.foo.bar 的修改不能触发副作用函数重新执行,这是为什么呢?来看一下现在的实现:
function reactive(obj) {
return new Proxy(obj ,{
get(target, key, receiver) {
if (key === 'raw')
return target;
track(target, key);
// 当读取属性值时,直接返回结果
return Reflect.get(target, key, receiver)
}
// 省略其他拦截函数
})
}
由上面这段代码可知,当我们读取 obj.foo.bar 时,首先要读取 obj.foo 的值。这里我们直接使用 Reflect.get 函数返回obj.foo 的结果。由于通过 Reflect.get 得到 obj.foo 的结果是一个普通对象,即 { bar: 1} ,它并不是一个响应式对象,所以在副作用函数中访问 obj.foo.bar 时,是不能建立响应联系的。要解决这个问题,我们需要对 Reflect.get 返回的结果做一层包装:
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
// If the result is an object, make it reactive
if (typeof result === 'object') {
return reactive(result);
}
return result;
},
// Other traps...
});
}
这段代码定义了一个名为reactive的函数,该函数接收一个对象作为参数,并返回该对象的代理。这个代理使用了get陷阱函数,当我们尝试获取对象的某个属性时,这个函数就会被触发。
在get陷阱函数中,我们首先使用Reflect.get方法获取目标对象的属性值。Reflect.get方法接收三个参数:目标对象、属性名和接收器对象。在这里,接收器对象就是代理对象本身。
然后,我们检查获取的结果是否为对象。如果是对象,我们就对其进行响应式处理,即再次调用reactive函数。这样做的目的是确保嵌套的对象也具有响应式特性,也就是说,当我们修改这些嵌套对象的属性时,也能触发响应式系统。
最后,如果获取的结果不是对象,我们就直接返回结果。
浅响应式
然而,并非所有情况下我们都希望深响应,这就催生了shallowReactive,即浅响应。所谓浅响应,指的是只有对象的第一层属性是响应的,例如:
例如,我们有一个对象,它的属性值也是一个对象:
let obj = {
innerObj: {
key: 'value'
}
}
如果我们对obj进行深响应处理:
let reactiveObj = reactive(obj);
那么,无论我们修改obj的属性,还是修改innerObj的属性,都会触发响应式系统:
reactiveObj.innerObj.key = 'new value'; // 触发响应式系统
但是,如果我们只想要obj的第一层属性是响应的,也就是说,只有当我们修改obj的属性时才触发响应式系统,而修改innerObj的属性则不触发,那么我们就需要使用shallowReactive函数:
let shallowReactiveObj = shallowReactive(obj);
这样,只有当我们修改obj的属性时,才会触发响应式系统:
shallowReactiveObj.innerObj = {}; // 触发响应式系统
shallowReactiveObj.innerObj.key = 'new value'; // 不触发响应式系统
Vuejs里reactive和shallowReactive
在Vue.js中,reactive和shallowReactive函数都用于创建响应式对象,这一小节来讨论下他们的不同。
reactive函数创建的是深度响应式对象。这意味着不仅对象本身,而且它内部的所有嵌套对象都会变成响应式的。无论是修改对象的属性,还是修改其嵌套对象的属性,都会触发响应式系统。
而shallowReactive函数创建的是浅层响应式对象。这意味着只有对象的顶层属性是响应式的。如果对象包含嵌套对象,那么修改这些嵌套对象的属性不会触发响应式系统。
let obj = {
innerObj: {
key: 'value'
}
}
let reactiveObj = Vue.reactive(obj);
reactiveObj.innerObj.key = 'new value'; // 这将触发响应式系统
let shallowReactiveObj = Vue.shallowReactive(obj);
shallowReactiveObj.innerObj.key = 'new value'; // 这将不会触发响应式系统
只读和浅只读
讨论完响应式和浅响应式,我们在来说下只读和浅只读:
Vue.js还提供了readonly
和shallowReadonly
函数,它们用于创建只读的响应式对象。
readonly
函数创建的是深度只读的响应式对象。这意味着不仅对象本身是只读的,而且它内部的所有嵌套对象也都是只读的。任何尝试修改对象或其嵌套对象的属性的操作都会失败。
shallowReadonly
函数创建的是浅层只读的响应式对象。这意味着只有对象的顶层属性是只读的。如果对象包含嵌套对象,那么这些嵌套对象的属性是可以修改的。
let obj = {
innerObj: {
key: 'value'
}
}
let readonlyObj = Vue.readonly(obj);
readonlyObj.innerObj.key = 'new value'; // 这将失败,因为对象是只读的
let shallowReadonlyObj = Vue.shallowReadonly(obj);
shallowReadonlyObj.innerObj.key = 'new value'; // 这将成功,因为只有顶层属性是只读的
評論