theme: fancy
highlight: a11y-light
纠正两个问题
function trigger(target, key) {
let effects = bucket?.get(target)?.get(key);
const effectsToRun = new Set(effects);
effects &&
effectsToRun.forEach((effectFn) => {
//开始
let deps = effectFn.deps;
deps.forEach((item) => {
item.delete(effectFn);
});
effectFn.deps.length = 0;
//结尾
//这一段代码应放到执行effectFn函数之前,如果放在这,后面的代码不需要执行,effectFn函数也不会触发,所以需要把这段代码换个位置
if (activeEffect !== effectFn) {
effectFn.options.scheduler
? effectFn.options.scheduler(effectFn)
: effectFn();
}
});
}
//放到effect函数内
function effect(fn, options = {
}) {
const effectFn = () => {
let deps = effectFn.deps;
deps.forEach((item) => {
item.delete(effectFn);
});
effectFn.deps.length = 0;
activeEffect = effectFn;
effectStack.push(activeEffect);
res=fn();//也就是以这个函数执行为分界线,在这之前清除副作用,赋值入栈,之后出栈赋值
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
//return fn();不能直接返回fn()会导致fn的立即执行
return res//用res存下来
};
effectFn.deps = [];
effectFn.options = options;
if (options.lazy) {
return effectFn;
} else {
effectFn();
}
}
实现watch
首先说明watch的实现也需要effect加调度器,先实现一个基础的watch
function watch(obj, cb) {
effect(() => obj.age, {
scheduler() {
cb();
},
});
}
watch(proxyData, () => {
console.log("changed");
});
proxyData.age++;
proxyData.age++;
上面只能监听一个属性,现在需要监听传入对象的任意属性都能触发
function watch(obj, cb) {
effect(
() => {
travel(obj)
},
{
scheduler() {
cb();
},
}
);
}
function travel(obj){
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key]!=null) {
travel(obj[key]);
}
}
}
watch的参数也能传入一个函数,实现也很简单,只需要简单判断执行
function watch(getter, cb) {
effect(
() => {
if (typeof getter === "function") {
getter();
} else {
travel(getter);
}
},
{
scheduler() {
cb();
},
}
);
}
那么怎么拿到watch的新值和旧值,可以借助之前的lazy,他保存着上次副作用函数执行的结果,也就是我们需要的旧值,并且watch不需要再执行副作用函数,所以使用lazy没有任何影响。
function watch(getter, cb) {
let newVal, oldVal;//新增
const effectFn = effect(
() => {
if (typeof getter === "function") {
return getter();//新增,这里要加个return,把值返回回来才能用lazy拿到
} else {
travel(getter);
}
},
{
lazy: true,//新增
scheduler() {
newVal = effectFn();//新增
cb(newVal, oldVal);
oldVal = newVal;//新增
},
}
);
oldVal = effectFn();//新增
}
watch还有一个参数immediate,在注册的时候就应该监听一次,之前在注册watch函数后等待数据改变触发副作用,现在直接在注册watch函数手动执行一次函数。实现:给watch添加第三个参数
watch(
() => proxyData.age,
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{
immediate: true }
);
function watch(getter, cb, options = {
}) {
let newVal, oldVal;
const job = () => {
//将调度函数抽离
newVal = effectFn();
cb(newVal, oldVal);
oldVal = newVal;
};
const effectFn = effect(
() => {
if (typeof getter === "function") {
return getter();
} else {
travel(getter);
}
},
{
lazy: true,
scheduler: job,//修改
}
);
if (options.immediate) {
//新增
job();
} else {
newVal = effectFn();
}
}
flush用于控制调度器的执行时机
在调度器函数内检测 options.flush 的值是否为post,如果是,则将 job 函数放到微任务队列中,从而实现异步延迟执行;否则直接执行 job 函数
watch(
() => proxyData.age,
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{
immediate: true, flush: "post" }
);
function watch(getter, cb, options = {
}) {
let newVal, oldVal;
const job = () => {
newVal = effectFn();
cb(newVal, oldVal);
oldVal = newVal;
};
const effectFn = effect(
() => {
if (typeof getter === "function") {
return getter();
} else {
travel(getter);
}
},
{
lazy: true,
scheduler() {
//修改
if (options.flush === "post") {
const p = Promise.resolve();
p.then(job);
} else {
job();
}
},
}
);
if (options.immediate) {
job();
} else {
newVal = effectFn();
}
}