实现 reactive 和 readonly 嵌套对象转换功能
reactive、 readonly代理的对象中还存在对象子结构,也要相应转换成proxy
还是老样子,TDD。
reactive.spec.ts
import {
isProxy, isReactive, reactive } from "../src/reactive";
describe("reactive", () => {
...
it("nested reactive", () => {
// 嵌套测试
const original = {
nestedObj: {
foo: 1,
},
array: [{
bar: 2 }],
};
const observed = reactive(original);
expect(isReactive(observed.nestedObj)).toBe(true);
expect(isReactive(observed.array)).toBe(true);
expect(isReactive(observed.array[0])).toBe(true);
});
});
readonly.spec.ts
import {
isProxy, isReadonly, readonly } from "../src/reactive";
describe("readonly", () => {
it("happy path", () => {
// set not be triggered
const original = {
prop: 1, bar: {
age: 10 } };
const wrapped = readonly(original);
expect(wrapped).not.toBe(original);
expect(isReadonly(wrapped)).toBe(true);
expect(isReadonly(original)).toBe(false);
// 嵌套测试
expect(isReadonly(wrapped.bar)).toBe(true);
expect(isReadonly(original.bar)).toBe(false);
expect(wrapped.prop).toBe(1);
});
});
上一章节讲到 reactive 和 readonly 通用逻辑提取到 baseHandlers 文件中
baseHandlers.ts
import {
extend, isObject } from "../../shared";
import {
track, trigger } from "./effect";
import {
reactive, ReactiveFlags, readonly } from "./reactive";
// 缓存一下get, set 后面始终用同一个
const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
function createGetter(isReadonly: Boolean = false, shallow: Boolean = false) {
return function get(target, key) {
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly;
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly;
}
const res = Reflect.get(target, key);
// 判断内部属性是否是 Object,是的话继续转换成 Proxy
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res);
}
if (!isReadonly) {
// 收集依赖
track(target, key);
}
return res;
};
}
function createSetter() {
return function set(target, key, value) {
const res = Reflect.set(target, key, value);
// 触发依赖
trigger(target, key);
return res;
};
}
export const mutableHandlers = {
get,
set,
};
export const readonlyHandlers = {
get: readonlyGet,
set(target, key, value) {
// 警告函数
console.warn(
`${
String(key)} cannot be set, because target is readonly, ${
target}`
);
return true;
},
};
测试一下,通过
最后
附上全部文章代码地址:mini-vue,欢迎交流探讨。