reactive和readonly嵌套
之前,我们通过reactive创建的响应式对象都是一些简单的对象,并不存在内部属性的值为object或者array,下面我们一起来看下面这种情况该如何处理。
编写测试
通过reactive创建一个较为复杂的响应式对象,并且我们期望这个对象里面的子对象也是一个响应式对象。
// src/reactivity/tests/reactive.spec.ts
describe("reactive", () => {
...
test("nested reactive", () => {
const original = {
nested: {
foo: 1
},
array: [{bar: 2}]
}
const observed = reactive(original)
expect(isReactive(observed.nested)).toBe(true)
expect(isReactive(observed.array)).toBe(true)
expect(isReactive(observed.array[0])).toBe(true)
})
})
通过readonly创建一个较为复杂的只读响应式对象,并且我们期望这个对象里面的子对象也是只读的。
// src/reactivity/tests/reactive.spec.ts
describe("readonly", () => {
it("happy path", () => {
...
expect(isReadonly(wrapped.bar)).toBe(true)
expect(isReadonly(original.bar)).toBe(false)
})
})
就目前的代码,这个测试肯定是通不过的,因为我们并没有针对这种情况做任何处理。
编码
下面,通过处理响应式对象的get请求,判断返回的结果是否为object,如果是,我们在外面包一层reactive就可以了。
1、创建一个公共的isObject函数
// src/shared/index.ts
...
export const isObject = (val) => {
return val !== null && typeof val === 'object'
}
2、在createGetter中进行判断
如果Reflect.get(target, key)的值是object,就返回调用reactive()的结果,这样就是一个响应式对象了。
// src/reactivity/baseHandlers.ts
import { isObject } from "../shared/index"
import { reactive, ReactiveFlags, readonly } from "./reactive"
...
function createGetter (isReadonly = false) {
return function get (target, key) {
...
if (isObject(res)) {
return reactive(res)
}
...
}
}
这样,reactive的嵌套就已经完成了,接下来,再来处理readonly的嵌套,其实这两者逻辑类似。
3、处理readonly嵌套逻辑
在上面,我们已经对返回的值做了判断,如果是object我们就进行包裹,其实readonly也是如此,下面,我们对createGetter进行改写
// src/reactivity/baseHandlers.ts
function createGetter (isReadonly = false) {
return function get (target, key) {
...
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
...
}
}
总结
这一节,我们通过判断get返回值是否为object,并通过isReadonly来确定是否是只读,然后通过reactive和readonly来包裹返回。