最近開了一個讀者回饋表單郵箱,無論是對文章的感想或是對部落格的感想,有什麼想回饋的都可以發郵箱跟我說:i_kkkp@163.com

Vue-浅响应与深响应

前言

介绍 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还提供了readonlyshallowReadonly函数,它们用于创建只读的响应式对象。

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'; // 这将成功,因为只有顶层属性是只读的
浅谈WebAssembly vue-Proxy和Reflect

評論