const bucket = new WeakMap();
let activeEffect = null;
function track(target, key){
if(!activeEffect) return;
let depsMap = bucket.get(target);
if(!depsMap){
bucket.set(target, depsMap=new Map())
}
let depsEffect = depsMap.get(key);
if(!depsEffect){
depsMap.set(key, depsEffect=new Set())
}
depsEffect.add(activeEffect);
activeEffect.deps.push(depsEffect);
}
function trigger(target, key){
const depsMap = bucket.get(target)
if(!depsMap) return;
const depsEffect = depsMap.get(key);
if(!depsEffect) return;
let effectsToRun = new Set();
depsEffect.forEach(effect=>{
if(effect!==activeEffect){
effectsToRun.add(effect)
}
})
effectsToRun.forEach(effect=>{
if(effect.option.scheduler){
effect.option.scheduler(effect)
}else{
effect();
}
})
}
function reactive(data){
const obj = new Proxy(data,{
get(target, key){
track(target, key);
return target[key]
},
set(target, key, value){
target[key] = value;
trigger(target, key);
return true;
}
})
return obj
}
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;
}
const effectStack = [];
function registerEffect (fn, option={}) {
function effectFn(){
activeEffect=effectFn;
effectStack.push(activeEffect);
cleanup(effectFn);
const res = fn()
effectStack.pop();
activeEffect = effectStack[effectStack.length-1];
return res
}
effectFn.deps = [];
effectFn.option = option;
if(!option.lazy){
effectFn();
}
return effectFn;
}
const data = {
name: '李翔',
age: 18,
ok: true
}
const obj = reactive(data);
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;
})
}
// registerEffect(()=>{
// console.log('执行effect1')
// registerEffect(()=>{
// console.log('执行effect2')
// obj.age
// })
// obj.name
// }, {
// scheduler(fn){
// setTimeout(()=>{
// fn()
// }, 1000)
// }
// })
// obj.name = "dora"
// obj.age = 20
registerEffect(()=>{
console.log(obj.age)
}, {
scheduler(fn){
jobQueue.add(fn);
flushJob();
}
})
obj.age++
obj.age++
obj.age++
obj.age++
function traverse(value, seen = new Set()){
//value可能是一个值,也可能是一个对象,先不考虑数组
if(typeof value !=='object' || seen.has(value) || value===null){
return
}
seen.add(value)
for(let k in value){
traverse(value[k], seen)
}
return value
}
function watch(source, cb, options={}){
let getter;
if(typeof source ==='function'){
//watch监听的是一个getter
getter = source;
}else{
getter = () => traverse(source)
}
let newVal, oldVal;
let cleanup;
const job = () => {
newVal = effectFn();
if(cleanup){
cleanup();
}
cb(newVal, oldVal, onInvalidate)
oldVal = newVal;
}
const effectFn = registerEffect(()=> getter()
,
{
lazy: true,
scheduler(){
if(options.flush==='post'){
const p = Promise.resolve();
p.then(job)
}else{
job();
}
}
}
)
if(options.immediate){
job()
}else{
oldVal = effectFn()
}
}
watch(obj, (newv, oldv, onInvalidate)=>{
let expired = false;
onInvalidate(()=>{
expired = true;
})
let data = 1;
setTimeout(()=>{
data = 2
}, 5000)
if(expired){
console.log(data);
}
})
watch(()=>obj.age, (newv, oldv)=>{
console.log({newv, oldv})
})
obj.name = 'aaa'
obj.age = 20
console