本文基于vue3,参考《vue.js设计与实现》
只将核心函数逻辑写出
let activeEffect;
const effectStack = [];
function effect(fn, options) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effect;
effectStack.push(effectFn);
const res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
effectFn.deps.length = 0;
}
}
function track(target, key) {
if (!activeEffect) return;
let depsMap = bucket.get(target);
if (!depsMap) {
bucket.set(target, (depsMap = new Map()));
}
let deps = depsMap.get(key);
1;
if (!deps) {
deps.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depsMap = bucket.get(target);
if (!depsMap) {
return;
}
const effects = depsMap.get(key);
effects &&
effects.forEach((effectFn) => {
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function traverse(value, seen = new Set()) {
// 读取值是原始值或已被读取,什么都不做
if (typeof value !== 'object' || value === null || seen.has(value)) {
return
}
// 将数据添加到seen中,代表已经读取过了,避免循环引用引起的死循环
seen.add(value)
// 只看原理,不考虑过多细节,此处暂不考虑数组等其他数据结构
// 假设value就是一个对象,递归处理
for (const k in value) {
traverse(value[k], seen)
}
return value
}
function watch(source, cb, options) {
let getter
if (typeof source === 'function') {
getter = source
} else {
getter = () => traverse(source)
}
let oldValue
let newValue
let cleanup
function onInvalidare(fn) {
cleanup = fn
}
const job = () => {
newValue = effectFn()
if (cleanup) {
cleanup
}
cb(newValue, oldValue, onInvalidare)
oldValue = newValue
}
const effectFn = effect(
() => getter,
{
lazy: true,
scheduler: () => {
if (options.flush === 'post') {
const p = Promise.resolve()
p.then(job)
} else {
job()
}
}
}
)
if (options.immediate) {
job()
} else {
oldValue = effectFn()
}
}
function computed(getter) {
let value;
// 缓存
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
// 调度器,getter中依赖的响应式变化时会执行调度器
scheduler() {
// 当计算属性依赖的响应式数据变化时,手动调用trigger触发响应式
trigger(obj, "value");
dirty = true;
},
});
const obj = {
get value() {
if (dirty) {
value = effectFn();
dirty = false;
}
},
};
return obj;
}