前置知识
Set、Map、WeakMap、Reflect、Proxy
使用
reactive 用来将需要观察的对象转换成可以观察的对象。
effect 定义了一个回调函数,当其中某个可观察对象发生变化时,触发回调。
import { reactive } from './reactive'
import { effect } from './effect'
const state = reactive({
count: 0,
age: 18
})
effect(() => {
console.log('effect: ' + state.count)
})
实现
Reactive
export function reactive(target) {
const observed = new Proxy(target, handler)
return observed
}
当要观察的数据结构是 Set, Map, WeakSet, WeakMap 时,用 collectionHandlers
others : baseHandlers
collectionHandlers 又分为 mutableCollectionHandlers、readonlyCollectionHandlers
baseHandlers 分 mutableHandlers、readonlyHandlers
export const mutableHandlers = {
get: createGetter(false),
set,
deleteProperty,
has,
ownKeys
};
function createGetter(isReadonly: boolean, shallow = false) {
return function get(target: object, key: string | symbol, receiver: object) {
const res = Reflect.get(target, key, receiver);
// 把 effect 的回调保存起来
track(target, TrackOpTypes.GET, key);
return isObject(res)
? isReadonly
? // need to lazy access readonly and reactive here to avoid
// circular dependency
readonly(res)
: reactive(res)
: res;
};
}
function set(target, key, value, receiver) {
const hadKey = hasOwn(target, key);
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
trigger(target, "add", key);
} else if (value !== oldValue) {
trigger(target, "set", key);
}
return result;
}
function deleteProperty(target, key) {
const hadKey = hasOwn(target, key);
const oldValue = target[key];
const result = Reflect.deleteProperty(target, key);
if (hadKey) {
trigger(target, "delete", key);
}
return result;
}
// 拦截判断对象是否具有某个属性时的操作,返回一个布尔值
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key);
track(target, TrackOpTypes.HAS, key);
return result;
}
// 拦截对象自身属性的读取操作 Object.keys() for...in
function ownKeys(target: object): (string | number | symbol)[] {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY);
return Reflect.ownKeys(target);
}
effect
现在还差 effect 的实现、get 中 track 的实现,set 中 trigger 的实现
存储数据的结构如下图所示。

const targetMap = new WeakMap();
const effectStack = []; // 记录 effect
export function effect(fn) {
const effect = run(effect, fn, null);
effect();
return effect;
}
function run(effect, fn, args) {
if (effectStack.indexOf(effect) === -1) {
try {
effectStack.push(effect);
return fn(...args);
} finally {
effectStack.pop();
}
}
}
export function track(target, operationType, key) {
const effect = effectStack[effectStack.length - 1];
if (effect) {
let depsMap = targetMap.get(target);
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (dep === void 0) {
depsMap.set(key, (dep = new Set()));
}
if (!dep.has(effect)) {
dep.add(effect);
}
}
}
track 就是将 effect 回调函数添加到其中使用的被观察对象的 set 集合中。当触发 trriger 函数时取出来触发回调。
export function trigger(target, operationType, key) {
const depsMap = targetMap.get(target);
if (depsMap === void 0) {
return;
}
const effects = new Set();
if (key !== void 0) {
// 将依赖这个key的所有监听函数推到相应队列中
const dep = depsMap.get(key);
dep &&
dep.forEach(effect => {
effects.add(effect);
});
}
if (operationType === "add" || operationType === "set") {
// 如果原始数据是数组,则key为length,否则为Symbol,这里最后会讲
const iterationKey = Array.isArray(target) ? "length" : Symbol("iterate");
const dep = depsMap.get(iterationKey);
dep &&
dep.forEach(effect => {
effects.add(effect);
});
}
effects.forEach(effect => {
effect();
});
}
当观察对象是 Set, Map, WeakSet, WeakMap 时使用 collectionHandlers
因为集合没有 set 方法,所以新创建了一个和集合对象具有相同属性和方法的普通对象,在集合对象 get 操作时将 target 对象换成新创建的普通对象。
const mutableInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, toReactive)
},
get size(this: IterableCollections) {
return size(this)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false)
}
为什么可以监听数组
其实是 Proxy 的能力,可以监听数组的 index 和 length
const arr = [0];
const obj = new Proxy(arr, {
get: function(target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function(target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
obj.push(0);

前置知识
Set、Map、WeakMap、Reflect、Proxy
使用
reactive 用来将需要观察的对象转换成可以观察的对象。
effect 定义了一个回调函数,当其中某个可观察对象发生变化时,触发回调。
实现
Reactive
当要观察的数据结构是 Set, Map, WeakSet, WeakMap 时,用 collectionHandlers
others : baseHandlers
collectionHandlers 又分为 mutableCollectionHandlers、readonlyCollectionHandlers
baseHandlers 分 mutableHandlers、readonlyHandlers
effect
现在还差 effect 的实现、get 中 track 的实现,set 中 trigger 的实现

存储数据的结构如下图所示。
track 就是将 effect 回调函数添加到其中使用的被观察对象的 set 集合中。当触发 trriger 函数时取出来触发回调。
当观察对象是 Set, Map, WeakSet, WeakMap 时使用 collectionHandlers
因为集合没有 set 方法,所以新创建了一个和集合对象具有相同属性和方法的普通对象,在集合对象 get 操作时将 target 对象换成新创建的普通对象。
为什么可以监听数组
其实是 Proxy 的能力,可以监听数组的 index 和 length