浅响应与深响应
本节中我们将要介绍reactive
和shallowReactive
的区别,即浅响应与深响应的区别。实际上,我们目前所实现的reactive
是浅响应的。拿如下代码来说:
1 | const obj = reactive({ foo: { bar: 1 } }) |
首先,创建 obj 代理对象,该对象的 foo 属性值也是一个对象,即{bar: 1}
。接着,在副作用函数内部访问 obj.foo.bar 的值。但是我们发现,后续对 obj.foo.bar 的修改不能触发副作用函数重新执行,这是为什么呢?我们来看一下现在的实现:
1 | function reactive(obj) { |
由上面这段代码可知,我们在读取 obj.foo.bar 时,首先要读取 obj.foo 的值。这里我们直接使用Reflect.get
函数返回 obj.foo 的结果。由于通过 Reflect.get 得到 obj.foo 的结果是一个普通对象,即{ bar: 1 },它并不是一个响应式对,所以在副作用函数中访问 obj.foo.bar 时,是不能建立响应联系的。要解决这个问题,我们需要对Reflect.get
返回的结果做一层包装:
1 | function reactive(obj) { |
如上代码所示,当读取属性值时,我们首先检查该值是否是对象,如果是对象,则递归调用reactive
函数将其包装成响应式数据并返回。这样当使用 obj.foo 读取 foo 属性值时,得到的就会是一个响应式数据,因此再通过 obj.foo.bar 读取 bar 属性值时,自然就会建立联系。这样,当修改 obj.foo.bar 的值时,就能够触发副作用函数重新执行了。
然而,并非所有情况下我们都希望深响应,这就催生了shallowReactive
,即浅响应。所谓浅响应,指的是只有对象的第一层属性是响应的,例如:
1 | const obj = shallowReactive({ foo: { bar: 1 } }) |
在这个例子中,我们使用shallowReactive
函数创建了一个浅响应的代理对象 obj。可以发现,只有对象的第一层属性是响应的,第二层及更深层次的属性则不是响应的。实现此功能也不难,如下面的代码所示:
1 | function createReactive(obj, isShallow = false) { |
在上面这段代码中,我们把创建对象的工作封装到一个新的函数createReactive
中。该函数除了接收原始对象 obj 以外,还接收第二个参数 isShallow,它是一个布尔值,代表是否创建浅响应对象。默认情况下,isShallow 的值为 false,代表创建深响应对象。这里需要注意的是,当读取属性操作发生时,在 get 拦截函数内如果发现是浅响应的,那么直接返回原始数据即可。有了createReactive
函数后,我们可以使用它轻松的实现 reactive 以及 shallowReactive 函数了:
1 | function reactive(obj) { |
代码
此章节修改的代码不多,主要是在reactive.js
中。如下面代码所示:
1 | /** |