reactive & readonly 对象嵌套功能
TIP
本篇笔记对应的分支号为: main分支: 26af116
截止目前为止,我们已经实现了 reactive
以及 readonly
功能。我们知道,在 Vue3
中,响应式对象内部嵌套的对象也应该是响应式的,那我们目前实现的功能是否符合这个特点呢?我们来一起验证下。
nested reactive
先来看下 reactive
,我们先来实现下它的测试用例:
// src/reactivity/__tests__/reactive.spec.ts
it('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)
})
我们可以看到,测试没有通过。这是为什么呢?
那是因为我们在获取对象属性时, createGetter
内部直接返回了 Reflect.get(target, key)
,但是没有判断该属性的值是否是个对象,也没有对其做相应的响应式处理。
因此,我们需要判断属性值的类型,如果是对象,则需要对其做响应式处理。我们先来定义一个 isObject
方法用于判断传入的值是否是对象类型,之后在 createGetter
中使用:
// src/reactivity/shared/index.ts
export function isObject(val) {
return val !== null && typeof(val) === 'object'
}
// src/reactivity/baseHandlers.ts
import { ReactiveFlags, reactive } from './reactive'
import { isObject } from '../shared'
/**
* 用于生成 get 方法
* @param isReadonly 是否是 readonly 对象
* @returns
*/
function createGetter(isReadonly = false) {
return function(target, key) {
// 判断是否是 reactive 对象
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 判断是否是 readonly 对象
return isReadonly
}
const res = Reflect.get(target, key)
if (isObject(res)) {
return reactive(res)
}
// 收集依赖
!isReadonly && track(target, key)
return res
}
}
这样一来,就符合了 响应式对象内部嵌套的对象也是响应式的 特征。
nested readonly
同理,我们再来看一看 readonly
的实现是否符合预期:
// src/reactivity/__tests__/readonly.spec.ts
it('happy path', () => {
const original = { foo: 1, bar: { baz: 2 }}
const wrapped = readonly(original)
expect(wrapped).not.toBe(original)
expect(wrapped.foo).toBe(1)
// 判断是否是 readonly 对象
expect(isReadonly(original)).toBe(false)
expect(isReadonly(original.bar)).toBe(false)
expect(isReadonly(wrapped.bar)).toBe(true)
expect(isReadonly(wrapped)).toBe(true)
})
哦豁,梅开二度!
其实道理是一样的,依旧是在 createGetter
时没有判断值类型。因此,我们只需要在刚才进行对象类型判断之处再加上 isReadonly
的判断即可。如果 isReadonly === true
且当前属性的值是对象类型,则返回 readonly(res)
:
// src/reactivity/baseHandlers.ts
/**
* 用于生成 get 方法
* @param isReadonly 是否是 readonly 对象
* @returns
*/
function createGetter(isReadonly = false) {
return function(target, key) {
// 省略一大波代码
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
}
}
Loading...