1.简单实现响应式
javascript
function effect() {
document.body.innerText = obj.text;
}
const bucket = new Set();
const data = { text: "hello world" };
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
bucket.forEach((fn) => fn());
return true;
},
});
effect();
setTimeout(() => {
obj.text = "hello vue3";
}, 1000);
function effect() {
document.body.innerText = obj.text;
}
const bucket = new Set();
const data = { text: "hello world" };
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
bucket.forEach((fn) => fn());
return true;
},
});
effect();
setTimeout(() => {
obj.text = "hello vue3";
}, 1000);
2.副作用函数的优化
javascript
function effect() {
document.body.innerText = obj.text;
}
const bucket = new Set();
const data = { text: "hello world" };
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
bucket.forEach((fn) => fn());
return true;
},
});
effect();
setTimeout(() => {
obj.text = "hello vue3";
}, 1000);
function effect() {
document.body.innerText = obj.text;
}
const bucket = new Set();
const data = { text: "hello world" };
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
bucket.forEach((fn) => fn());
return true;
},
});
effect();
setTimeout(() => {
obj.text = "hello vue3";
}, 1000);
3.依赖收集
javascript
const buckct = new WeakMap();
let activeEffect;
const data = { text: "hello world" };
//依赖函数
function effect(fn) {
activeEffect = fn;
fn();
}
// 代理对象
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
// weakMap 由target和map构成
// map 由key 和 set构成
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1,f2,f3] type:set
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key);
effects && effects.forEach((fn) => fn());
}
// 第一次收集依赖
effect(() => {
console.log("effect fn");
document.body.innerText = obj.text;
// 第一次读取触发代理对象的读取操作,并将effect函数建立和obj依赖
});
setTimeout(() => {
obj.text = "hello vue3";
// obj.name ='我是没有建立依赖的属性呀' // 由于新的属性没有建立对应的依赖,所以函数不会在此执行
}, 1000);
const buckct = new WeakMap();
let activeEffect;
const data = { text: "hello world" };
//依赖函数
function effect(fn) {
activeEffect = fn;
fn();
}
// 代理对象
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
// weakMap 由target和map构成
// map 由key 和 set构成
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1,f2,f3] type:set
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key);
effects && effects.forEach((fn) => fn());
}
// 第一次收集依赖
effect(() => {
console.log("effect fn");
document.body.innerText = obj.text;
// 第一次读取触发代理对象的读取操作,并将effect函数建立和obj依赖
});
setTimeout(() => {
obj.text = "hello vue3";
// obj.name ='我是没有建立依赖的属性呀' // 由于新的属性没有建立对应的依赖,所以函数不会在此执行
}, 1000);
4.引入切换分支与 cleanup
javascript
const buckct = new WeakMap();
let activeEffect;
const data = { ok: true, text: "hello world" };
//依赖函数
function effect(fn) {
activeEffect = fn;
fn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
// weakMap 由target和map构成
// map 由key 和 set构成
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1,f2,f3] type:set
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key);
effects && effects.forEach((fn) => fn());
}
effect(() => {
console.log("effect fn");
document.body.innerText = obj.ok ? obj.text : "not";
});
obj.ok = false;
obj.text = "hello dadada";
// 当obj.ok 属性为false的时候,map中依然存在 ok 和 text的2个依赖,按理来说我们只需要ok的依赖,而不需要set的依赖,以为当我们执行set依赖的时候页面并不会刷新内容, 而且当我们修改obj.text值得时候,仍然会导致副作用函数重复执行,即使document.body.innertext值不需要改变
// 既 理想状态下副作用函数不应该被字段obj.text收集依赖
console.log(buckct);
// 我们在下节探讨如何消除这种依赖
const buckct = new WeakMap();
let activeEffect;
const data = { ok: true, text: "hello world" };
//依赖函数
function effect(fn) {
activeEffect = fn;
fn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
// weakMap 由target和map构成
// map 由key 和 set构成
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1,f2,f3] type:set
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key);
effects && effects.forEach((fn) => fn());
}
effect(() => {
console.log("effect fn");
document.body.innerText = obj.ok ? obj.text : "not";
});
obj.ok = false;
obj.text = "hello dadada";
// 当obj.ok 属性为false的时候,map中依然存在 ok 和 text的2个依赖,按理来说我们只需要ok的依赖,而不需要set的依赖,以为当我们执行set依赖的时候页面并不会刷新内容, 而且当我们修改obj.text值得时候,仍然会导致副作用函数重复执行,即使document.body.innertext值不需要改变
// 既 理想状态下副作用函数不应该被字段obj.text收集依赖
console.log(buckct);
// 我们在下节探讨如何消除这种依赖
5.完善切换分支
javascript
const buckct = new WeakMap();
const data = { ok: true, text: "hello world" };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
fn(); // 收集依赖``
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
// console.log(effects)
const effectsToRun = new Set(effects);
// console.log(...effectsToRun)
effectsToRun.forEach((effectFn) => {
// console.log(effectFn)
effectFn();
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
// console.log(deps) // deps => Set[f]
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
effect(() => {
console.log("effect fn");
document.body.innerText = obj.ok ? obj.text : "not";
});
// 当第一次effect函数执行完毕之后(收集依赖的过程)会对obj.ok obj.text 收集依赖, 并且activeEffect.deps(数组)中包含2个set依赖(即2个属性的依赖)
obj.ok = false;
// console.log(activeEffect.deps)
obj.text = "dada";
// obj.text ='dadadadad'
const buckct = new WeakMap();
const data = { ok: true, text: "hello world" };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
fn(); // 收集依赖``
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
// console.log(effects)
const effectsToRun = new Set(effects);
// console.log(...effectsToRun)
effectsToRun.forEach((effectFn) => {
// console.log(effectFn)
effectFn();
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
// console.log(deps) // deps => Set[f]
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
effect(() => {
console.log("effect fn");
document.body.innerText = obj.ok ? obj.text : "not";
});
// 当第一次effect函数执行完毕之后(收集依赖的过程)会对obj.ok obj.text 收集依赖, 并且activeEffect.deps(数组)中包含2个set依赖(即2个属性的依赖)
obj.ok = false;
// console.log(activeEffect.deps)
obj.text = "dada";
// obj.text ='dadadadad'
6.嵌套的 effect.js
javascript
const buckct = new WeakMap();
const data = { foo: true, bar: true };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set(effects);
effectsToRun.forEach((effectFn) => {
effectFn();
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
debugger;
effect(function effectFn1() {
console.log("effectFn1 执行");
effect(function effectFn2() {
console.log("effectFn2 执行");
temp2 = obj.bar;
});
// 当执行完内部嵌套的effect函数时内层副作用函数的执行会覆盖activeEffect的值并且对obj.bar产生依赖效果
temp1 = obj.foo;
});
// 当effect函数执行完毕后activeEffect.deps数组中包含2个set依赖,此时已经收集到了map和set的依赖,并且activeEffect.deps收集的副作用用函数是effect的内层函数,所以哪怕当我们访问外层函数进行依赖的操作,收集和触发的副作用函数都会是内层副作用函数.effectFn函数执行完毕后为
obj.foo = "dasdas";
// 当这个执行完毕之后activeEffect.deps中只有对于bar的依赖
const buckct = new WeakMap();
const data = { foo: true, bar: true };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set(effects);
effectsToRun.forEach((effectFn) => {
effectFn();
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
debugger;
effect(function effectFn1() {
console.log("effectFn1 执行");
effect(function effectFn2() {
console.log("effectFn2 执行");
temp2 = obj.bar;
});
// 当执行完内部嵌套的effect函数时内层副作用函数的执行会覆盖activeEffect的值并且对obj.bar产生依赖效果
temp1 = obj.foo;
});
// 当effect函数执行完毕后activeEffect.deps数组中包含2个set依赖,此时已经收集到了map和set的依赖,并且activeEffect.deps收集的副作用用函数是effect的内层函数,所以哪怕当我们访问外层函数进行依赖的操作,收集和触发的副作用函数都会是内层副作用函数.effectFn函数执行完毕后为
obj.foo = "dasdas";
// 当这个执行完毕之后activeEffect.deps中只有对于bar的依赖
7.避免无线递归循环
javascript
const buckct = new WeakMap();
const data = { foo: 1 };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set();
effects &&
effectsToRun.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
});
effectsToRun.forEach((effctFn) => effctFn());
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
effect(() => obj.foo++);
// effect(()=>{
// obj.foo = obj.foo + 1
// })
const buckct = new WeakMap();
const data = { foo: 1 };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set();
effects &&
effectsToRun.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
});
effectsToRun.forEach((effctFn) => effctFn());
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
effect(() => obj.foo++);
// effect(()=>{
// obj.foo = obj.foo + 1
// })
8.调度执行
javascript
const buckct = new WeakMap();
const data = { foo: 1 };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
// 将options挂载到 effectFn上
effectFn.options = options;
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set();
//如果trigger触发的函数与当前正在执行的副作用函数相同,则不触发执行
effects &&
effects.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
});
effectsToRun.forEach((effctFn) => {
if (effctFn.options.scheduler) {
effctFn.options.scheduler(effctFn);
} else {
effctFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
function flushJob() {
if (isFlushing) return;
isFlushing = true;
p.then(() => {
jobQueue.forEach((job) => job());
}).finally(() => {
isFlushing = false;
});
}
// effect(
// ()=>{
// console.log(obj.foo)
// },
// // options
// {
// scheduler(fn) {
// setTimeout(fn)
// }
// }
// )
effect(
() => {
console.log(obj.foo);
},
// options
{
scheduler(fn) {
jobQueue.add(fn);
flushJob();
},
}
);
obj.foo++;
obj.foo++;
// console.log('结束了')
const buckct = new WeakMap();
const data = { foo: 1 };
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
let temp1, temp2;
// effect栈
const effectStack = [];
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 在调用副作用函数之前把当前副作用函数压入栈中
effectStack.push(activeEffect);
fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
};
// 将options挂载到 effectFn上
effectFn.options = options;
effectFn.deps = [];
effectFn();
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
const effectsToRun = new Set();
//如果trigger触发的函数与当前正在执行的副作用函数相同,则不触发执行
effects &&
effects.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
});
effectsToRun.forEach((effctFn) => {
if (effctFn.options.scheduler) {
effctFn.options.scheduler(effctFn);
} else {
effctFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
function flushJob() {
if (isFlushing) return;
isFlushing = true;
p.then(() => {
jobQueue.forEach((job) => job());
}).finally(() => {
isFlushing = false;
});
}
// effect(
// ()=>{
// console.log(obj.foo)
// },
// // options
// {
// scheduler(fn) {
// setTimeout(fn)
// }
// }
// )
effect(
() => {
console.log(obj.foo);
},
// options
{
scheduler(fn) {
jobQueue.add(fn);
flushJob();
},
}
);
obj.foo++;
obj.foo++;
// console.log('结束了')
9.调度执行-lazy
javascript
const buckct = new WeakMap();
const data = { foo: 1, bar: 2 };
const effectStack = [];
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 将外层副作用函数压入栈中
effectStack.push(effectFn);
// 将 res 的结果
const res = fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
// console.log(effects)
const effectToRun = new Set();
effects.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
// console.log(deps) // deps => Set[f]
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
function flushJob() {
if (isFlushing) return;
isFlushing = true;
p.then(() => {
jobQueue.forEach((job) => job());
}).finally(() => {
isFlushing = false;
});
}
debugger;
const effectFn = effect(() => obj.foo + obj.bar, { lazy: true });
// effect函数返回值为effectFn函数
const value = effectFn();
// console.log('end')
console.log(value);
const buckct = new WeakMap();
const data = { foo: 1, bar: 2 };
const effectStack = [];
// 此时得activeEffect是一个函数,有一个数组得属性
let activeEffect;
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
// 将外层副作用函数压入栈中
effectStack.push(effectFn);
// 将 res 的结果
const res = fn(); // 收集依赖``
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = buckct.get(target);
if (!depMap) {
buckct.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // [f1=>[],f2=>[],f3=>[]] type:set
activeEffect.deps.push(deps); //
}
function trigger(target, key) {
// 判断对象有没有和自己的属性建立依赖,如果不存在则直接终止函数
const depMap = buckct.get(target);
if (!depMap) return; // 判断如果没有建立联系的属性则不执行渲染函数
const effects = depMap.get(key); // [f1=>[],f2=>[],f3=>[]] type:set
// console.log(effects)
const effectToRun = new Set();
effects.forEach((effectFn) => {
if (effectFn !== activeEffect) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
// console.log(deps) // deps => Set[f]
deps.delete(effectFn);
}
// 重置effectFn.deps数组
effectFn.deps.length = 0;
}
const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
function flushJob() {
if (isFlushing) return;
isFlushing = true;
p.then(() => {
jobQueue.forEach((job) => job());
}).finally(() => {
isFlushing = false;
});
}
debugger;
const effectFn = effect(() => obj.foo + obj.bar, { lazy: true });
// effect函数返回值为effectFn函数
const value = effectFn();
// console.log('end')
console.log(value);
10.实现计算属性
javascript
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
debugger;
const sumRes = computed(() => obj.foo + obj.bar);
effect(() => {
console.log(sumRes.value);
});
obj.foo++;
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
debugger;
const sumRes = computed(() => obj.foo + obj.bar);
effect(() => {
console.log(sumRes.value);
});
obj.foo++;
11.watch 实现
javascript
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects &&
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// // watch函数接收2个参数,一个是响应式数据,一个是响应的回调函数
// function watch(source,cb) {
// effect(
// // 调用traverse函数递归读取
// ()=>traverse(source), // 触发读取操作,从而建立联系
// {
// scheduler() {
// cb()
// }
// }
// )
// }
//
function traverse(value, seen = new Set()) {
// 如果读取的数据是原始值,或者已经被读取过,那么return
if (typeof value !== "object" || value === null || seen.has(value)) return;
seen.add(value); // Set[1] proxy
for (const key in value) {
traverse(value[key], seen); // 递归调用这个对象的每一个值,因为可能对象的属性还是一个对象。
// 这样就可以对对象内部所有的属性进行依赖
}
return value; // Proxy{foo: 1, bar: 2}
}
//
// watch(obj,()=>{
// console.log('数据发生改变')
// })
// obj.foo++
// obj.bar++
function watch(source, cb) {
let getter;
if (typeof source === "function") {
getter = source;
} else {
getter = () => traverse(source);
}
let oldValue, newValue;
const effectFn = effect(
() => getter(), // 对用户自己传入得数据进行依赖收集
{
lazy: true,
scheduler() {
// 在scheduler刷新之前执行副作用函数得到的是新值
newValue = effectFn();
// 将旧值和新值作为回调函数参数
cb(newValue, oldValue);
// 更新旧值,不然下次会得到错误的旧值
oldValue = newValue;
},
}
);
// 手动调用副作用函数,拿到的就是旧值
oldValue = effectFn(); // 1
}
debugger;
watch(
() => obj.foo,
(newValue, oldValue) => {
console.log(`新值:${newValue}--旧值:${oldValue}`);
// console.log('数据发生改变')
}
);
// 当wacth函数执行完毕之后,会对我们传入得getter中得数据进行收集依赖,由于我们手动调用了oldValue = effectFn()则会拿到这个数据的返回值(旧值:1)
obj.foo++;
// 首先会触发tack函数,由于我们之前清理了activeEffect,所以不执行,然后执行trigger函数(此时obj.foo = 2),然后由于scheduler存在,所以执行scheduler函数 里面执行effectFn => 然后在里面执行fn函数 也就是getter函数()=>obj.foo(这里foo为2,所以重新进行依赖收集),effectFn函数执行完毕拿到fn的返回值为2 也就是newValue => cb(newValue,oldValue)
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects &&
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// // watch函数接收2个参数,一个是响应式数据,一个是响应的回调函数
// function watch(source,cb) {
// effect(
// // 调用traverse函数递归读取
// ()=>traverse(source), // 触发读取操作,从而建立联系
// {
// scheduler() {
// cb()
// }
// }
// )
// }
//
function traverse(value, seen = new Set()) {
// 如果读取的数据是原始值,或者已经被读取过,那么return
if (typeof value !== "object" || value === null || seen.has(value)) return;
seen.add(value); // Set[1] proxy
for (const key in value) {
traverse(value[key], seen); // 递归调用这个对象的每一个值,因为可能对象的属性还是一个对象。
// 这样就可以对对象内部所有的属性进行依赖
}
return value; // Proxy{foo: 1, bar: 2}
}
//
// watch(obj,()=>{
// console.log('数据发生改变')
// })
// obj.foo++
// obj.bar++
function watch(source, cb) {
let getter;
if (typeof source === "function") {
getter = source;
} else {
getter = () => traverse(source);
}
let oldValue, newValue;
const effectFn = effect(
() => getter(), // 对用户自己传入得数据进行依赖收集
{
lazy: true,
scheduler() {
// 在scheduler刷新之前执行副作用函数得到的是新值
newValue = effectFn();
// 将旧值和新值作为回调函数参数
cb(newValue, oldValue);
// 更新旧值,不然下次会得到错误的旧值
oldValue = newValue;
},
}
);
// 手动调用副作用函数,拿到的就是旧值
oldValue = effectFn(); // 1
}
debugger;
watch(
() => obj.foo,
(newValue, oldValue) => {
console.log(`新值:${newValue}--旧值:${oldValue}`);
// console.log('数据发生改变')
}
);
// 当wacth函数执行完毕之后,会对我们传入得getter中得数据进行收集依赖,由于我们手动调用了oldValue = effectFn()则会拿到这个数据的返回值(旧值:1)
obj.foo++;
// 首先会触发tack函数,由于我们之前清理了activeEffect,所以不执行,然后执行trigger函数(此时obj.foo = 2),然后由于scheduler存在,所以执行scheduler函数 里面执行effectFn => 然后在里面执行fn函数 也就是getter函数()=>obj.foo(这里foo为2,所以重新进行依赖收集),effectFn函数执行完毕拿到fn的返回值为2 也就是newValue => cb(newValue,oldValue)
12.watch 后续补充
javascript
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects &&
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// // watch函数接收2个参数,一个是响应式数据,一个是响应的回调函数
// function watch(source,cb) {
// effect(
// // 调用traverse函数递归读取
// ()=>traverse(source), // 触发读取操作,从而建立联系
// {
// scheduler() {
// cb()
// }
// }
// )
// }
//
function traverse(value, seen = new Set()) {
// 如果读取的数据是原始值,或者已经被读取过,那么return
if (typeof value !== "object" || value === null || seen.has(value)) return;
seen.add(value); // Set[1] proxy
for (const key in value) {
traverse(value[key], seen); // 递归调用这个对象的每一个值,因为可能对象的属性还是一个对象。
// 这样就可以对对象内部所有的属性进行依赖
}
return value; // Proxy{foo: 1, bar: 2}
}
//
// watch(obj,()=>{
// console.log('数据发生改变')
// })
// obj.foo++
// obj.bar++
function watch(source, cb, options = {}) {
let getter;
if (typeof source === "function") {
getter = source;
} else {
getter = () => traverse(source);
}
let oldValue, newValue;
const job = () => {
newValue = effectFn();
cb(newValue, oldValue);
oldValue = newValue;
};
const effectFn = effect(() => getter(), {
lazy: true,
scheduler: job,
});
if (options.immediate) {
job();
} else {
oldValue = effectFn();
}
}
watch(
() => obj.foo,
(newValue, oldValue) => {
console.log(`新值:${newValue}--旧值:${oldValue}`);
// console.log('数据发生改变')
},
{
immediate: true,
}
);
obj.foo++;
const data = { foo: 1, bar: 2 };
let activeEffect;
const effectStack = [];
const bucket = new WeakMap();
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn);
activeEffect = effectFn;
effectStack.push(effectFn);
let res = fn();
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
return res;
};
effectFn.options = options;
effectFn.deps = [];
if (!options.lazy) {
effectFn();
}
return effectFn;
}
const obj = new Proxy(data, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
},
});
function track(target, key) {
if (!activeEffect) return;
let depMap = bucket.get(target);
if (!depMap) {
bucket.set(target, (depMap = new Map()));
}
let deps = depMap.get(key);
if (!deps) {
depMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
function trigger(target, key) {
const depMap = bucket.get(target);
if (!depMap) return;
const effects = depMap.get(key);
const effectToRun = new Set();
effects &&
effects.forEach((effectFn) => {
if (activeEffect !== effectFn) {
effectToRun.add(effectFn);
}
});
effectToRun.forEach((effectFn) => {
if (effectFn.options.scheduler) {
// 如果有条度器,则把副作用函数放到scheduer中执行
effectFn.options.scheduler(effectFn);
} else {
effectFn();
}
});
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]; // Set
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// computed计算属性是我们封装的一个代码执行库,getter函数实际就是我们的副作用执行函数,我们用effectFn来包裹
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true;
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发响应
trigger(obj, value);
},
});
const obj = {
get value() {
// 只对脏值时才计算,并将得到的值放到value中
if (dirty) {
value = effectFn();
dirty = false; // 下次如果依赖的数据没有发生改变那么则下次不需要再次加载
}
// 当读取value时,手动调用tacj函数进行追踪
track(obj, value);
return value;
},
};
return obj;
}
// // watch函数接收2个参数,一个是响应式数据,一个是响应的回调函数
// function watch(source,cb) {
// effect(
// // 调用traverse函数递归读取
// ()=>traverse(source), // 触发读取操作,从而建立联系
// {
// scheduler() {
// cb()
// }
// }
// )
// }
//
function traverse(value, seen = new Set()) {
// 如果读取的数据是原始值,或者已经被读取过,那么return
if (typeof value !== "object" || value === null || seen.has(value)) return;
seen.add(value); // Set[1] proxy
for (const key in value) {
traverse(value[key], seen); // 递归调用这个对象的每一个值,因为可能对象的属性还是一个对象。
// 这样就可以对对象内部所有的属性进行依赖
}
return value; // Proxy{foo: 1, bar: 2}
}
//
// watch(obj,()=>{
// console.log('数据发生改变')
// })
// obj.foo++
// obj.bar++
function watch(source, cb, options = {}) {
let getter;
if (typeof source === "function") {
getter = source;
} else {
getter = () => traverse(source);
}
let oldValue, newValue;
const job = () => {
newValue = effectFn();
cb(newValue, oldValue);
oldValue = newValue;
};
const effectFn = effect(() => getter(), {
lazy: true,
scheduler: job,
});
if (options.immediate) {
job();
} else {
oldValue = effectFn();
}
}
watch(
() => obj.foo,
(newValue, oldValue) => {
console.log(`新值:${newValue}--旧值:${oldValue}`);
// console.log('数据发生改变')
},
{
immediate: true,
}
);
obj.foo++;