watch实现
const data = {text: 'hello world', ok: true, foo: 1, bar: 2};
const bucket = new WeakMap();
let activeEffect;
let effectStack = [];
const obj = new Proxy(data, {
get(target, key){
track(target, key);
return target[key];
},
set(target, key, newVal) {
target[key] = newVal;
trigger(target, key);
}
})
function track(target, key){
if(!activeEffect) return target[key];
let depsMap = bucket.get(target);
if(!depsMap) {
bucket.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(key);
if(!deps) {
depsMap.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);
const effectsSet = new Set();
effects&& effects.forEach(fn => {
if(activeEffect !== fn) {
effectsSet.add(fn);
}
})
effectsSet.forEach(fn => {
fn.options.scheduler ? fn.options.scheduler(fn) : fn();
});
}
function cleanup(effectFn) {
for(let i=0; i< effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
const effect = (fn, options={})=>{
const effectFn = () =>{
cleanup(effectFn);
activeEffect = effectFn;
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 watch(source, cb, options= {}) {
let getter, newVal, oldVal;
if(typeof source === 'function'){
getter = source;
}else {
getter = ()=>traverse(source);
}
const job = ()=>{
newVal = effectFn();
cb(newVal, oldVal);
oldVal = newVal;
}
const effectFn = effect(()=> getter(), {
lazy: true,
scheduler: job
})
if(options.immediate) {
job();
}else{
oldVal = effectFn();
}
}
function traverse(value, seen = new Set()) {
if(typeof value !== "object" || value === null || seen.has(value)) return;
seen.add(value); // 防止死循环
for (const key in value) {
traverse(value[key], seen);
}
return value;
}
watch(()=>obj.text, (newVal, oldVal)=>{console.log(newVal)});
obj.text = 'hello i';
obj.text = 'hello j';
执行结果
加上immediate参数让其立即执行
watch(()=>obj.text, (newVal, oldVal)=>{console.log(newVal)}, {immediate: true});
执行结果