reactiveEffect
大约 2 分钟
引入
vue2.x
的响应式 是通过 Watcher
, Observer
, Dep
来实现的 即 观察者模式-发布订阅模式
Observer
通过 Object.defineProperty
来劫持对象的 key
Dep
即依赖, 主要做两件事, get
时 收集依赖, set
时通知 Watcher
Watcher
接收到通知 调用自己的 update
去 触发 render
(不一定都是render
, 只是举个例子)
那么 vue3.x
呢?
trigger
,track
实现了派发更新
与收集依赖
targetMap
实现了 依赖的对应关系reactiveEffect
就是实现了调用render
(举例)部分
简单实现
初始化项目
pnpm init
pnpm i rollup -D
pnpm i rollup-typescript2 typescript -D
npx tsc --init
src/index.ts
console.log(123);
rollup.config.mjs
import typescript from 'rollup-plugin-typescript2';
export default {
input: 'src/index.ts',
output: {
file: 'dist/bundle.js',
format: 'cjs',
},
plugins: [typescript()],
};
package.json
{
"scripts": {
"build": "rollup --config"
}
}
有报错自己看报错信息
执行 pnpm run build
看是否生成了 dist/bundle.js
Proxy 基础代码, 以及 reactive 简单实现
src/baseHandlers.ts
import { track, trigger } from './effect';
function get(target: object, key: string | symbol, receiver: object) {
const res = Reflect.get(target, key, receiver);
track(target, key);
return res;
}
function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object,
): boolean {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key);
return result;
}
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
};
src/reactive.ts
import { mutableHandlers } from './baseHandlers';
export const reactiveMap = new WeakMap<object, any>();
export function reactive<T extends object>(target: T): T;
export function reactive(target: object) {
if (target !== null && typeof target !== 'object') {
return target;
}
const existingProxy = reactiveMap.get(target);
if (existingProxy) {
return existingProxy;
}
const proxy = new Proxy(target, mutableHandlers);
reactiveMap.set(target, proxy);
return proxy;
}
reactiveEffect 实现
- 接收一个参数
fn
- 需要有一个
run
方法 去执行 那个fn
- 还需要指明一个全局的
activeReactiveEffect
方便被收集
class ReactiveEffect<T = any> {
constructor(public fn: () => T) {}
run() {
try {
activeEffect = this;
return this.fn();
} finally {
activeEffect = undefined;
}
}
}
effect 实现
effect
应该和reactiveEffect
一样接收一个fn
effect
需要立刻执行effect
需要返回一个runner
export interface ReactiveEffectRunner<T = any> {
(): T;
effect: ReactiveEffect;
}
export function effect<T = any>(fn: () => T): ReactiveEffectRunner {
if ((fn as ReactiveEffectRunner).effect) {
fn = (fn as ReactiveEffectRunner).effect.fn;
}
const _effect = new ReactiveEffect(fn);
_effect.run();
const runner = _effect.run.bind(_effect) as ReactiveEffectRunner;
runner.effect = _effect;
return runner;
}
实现 track
- 接收一个
对象
和 触发的key
- 获取
key
对应 的dep
, 然后 将当前的activeEffect
收集进去
export function track(target: object, key: unknown) {
if (activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep!.add(activeEffect!);
}
}
实现 trigger
- 接收一个
对象
和 触发的key
- 获取
key
对应 的dep
, 存进deps
- 遍历 deps, 并执行
reactiveEffect
的run
方法
export function trigger(target: object, key?: unknown) {
const depsMap = targetMap.get(target);
if (!depsMap) {
return;
}
let deps: (Dep | undefined)[] = [];
if (key !== void 0) {
deps.push(depsMap.get(key));
}
if (deps.length === 1) {
if (deps[0]) {
triggerEffects(deps[0]);
}
} else {
const effects: ReactiveEffect[] = [];
for (const dep of deps) {
if (dep) {
effects.push(...dep);
}
}
triggerEffects(new Set(effects));
}
}
export function triggerEffects(dep: Dep | ReactiveEffect[]) {
const effects = Array.isArray(dep) ? dep : [...dep];
for (const effect of effects) {
if (effect !== activeEffect) {
effect.run();
}
}
}