reactive、shallowReactive、readonly、shallowReadonly 区别只是在是否仅读、是否深度 –> 柯里化
reactivity 模块的使用
<script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<script>
const { reactive, shallowReactive, readonly, shallowReadonly } =
VueReactivity;
const state = reactive({ name: "cherish", age: { age: 10 } });
state.age = 12;
console.log(state.age);
</script>
复制代码
实现 4 个核心 Api
打开 sourceMap
tsconfig.json
{
"compilerOptions": {
"target": "ESNEXT",
"module": "ESNEXT",
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@vue/*": ["packages/*/src"]
},
+ "sourceMap": true
}
}
复制代码
目录结构
├── example
│ └── 1.reactivity-api.html
├── package.json
├── packages
│ ├── reactivity
│ │ ├── package.json
│ │ └── src
│ │ ├── baseHandlers.ts
│ │ ├── index.ts
│ │ └── reactive.ts
│ ├── runtime-core
│ ├── runtime-dom
│ └── shared
│ ├── package.json
│ └── src
│ └── index.ts
├── rollup.config.js
├── scripts
│ ├── build.js
│ └── dev.js
└── tsconfig.json
复制代码
入口引入
packages/reactivity/src/index.js
export {
reactive,
shallowReactive,
shallowReadonly,
readonly,
} from "./reactive";
复制代码
柯里化拆分
packages/reactivity/src/reactive.js
抽取公共的 createReactiveObject 方法
- reactive 只拦截对象类型(ref 可以是普通类型)。
- 已被代理过的对象不再代理(可能一个对象被代理是深度,又被仅读代理了)。
- 做一个映射表,看对象有没有把被代理过。WeakMap 会自动垃圾回收,不会造成内存泄漏,存储的 key 只能是对象。
import { isObject } from "@vue/shared";
import {
mutableHandlers,
shallowReactiveHandlers,
readonlyHandlers,
shallowReadonlyHandlers,
} from "./baseHandlers";
export function reactive(target) {
return createReactiveObject(target, false, mutableHandlers);
}
export function shallowReactive(target) {
return createReactiveObject(target, false, shallowReactiveHandlers);
}
export function readonly(target) {
return createReactiveObject(target, true, readonlyHandlers);
}
export function shallowReadonly(target) {
return createReactiveObject(target, true, shallowReadonlyHandlers);
}
const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
export function createReactiveObject(target, isReadonly, baseHandlers) {
if (!isObject(target)) return target;
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existProxy = proxyMap.get(target);
if (existProxy) return existProxy;
const proxy = new Proxy(target, baseHandlers);
proxyMap.set(target, proxy); // 将要代理的对象和对应代理结果缓存起来
return proxy;
}
复制代码
baseHandlers.ts
import { isObject } from "@vue/shared/src";
import { reactive, readonly } from "./reactive";
debugger;
const createGetter =
(isReadonly = false, shallow = false) =>
(target, key, receiver) => {
const res = Reflect.get(target, key, receiver);
if (!isReadonly) {
// 收集依赖,数据变化后更新视图
console.log("执行effect时会取值", "收集effect");
}
if (shallow) return res;
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res);
}
return res;
};
const createSetter =
(shallow = false) =>
(target, key, value, receiver) => {
const result = Reflect.set(target, key, value, receiver);
return result;
};
const get = createGetter();
const shallowGet = createGetter(false, true);
const readonlyGet = createGetter(true);
const showllowReadonlyGet = createGetter(true, true);
const set = createSetter();
const shallowSet = createSetter(true);
const readonlySet = {
set: (_, key) => console.warn(`set on key ${key} falied`),
};
export const mutableHandlers = { get, set };
export const shallowReactiveHandlers = { get: shallowGet, set: shallowSet };
export const readonlyHandlers = { get: readonlyGet, set: readonlySet };
export const shallowReadonlyHandlers = {
get: showllowReadonlyGet,
set: readonlySet,
};
复制代码
reflect 的好处
- 后续
Object
上的方法会被迁移到Reflect
, 如Reflect.getPropertyOf()
- 以前
target[key] = value
方式设置值可能会失败,并不会报异常,也没有返回值标识,Reflect
方法有返回值告诉用户是否设置成功。
ps. 一般proxy
会配合Reflect
,而使用 Reflect
可以不使用 proxy
vue2 & vue3 比较
vue2
: 一上来就递归vue3
: 取值时才会代理–懒代理
设计思想
柯里化,组合优于继承
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END