function isObject(target) {
return typeof target === 'object' && target !== null
}
function hasOwn(target, key) {
return Reflect.has(target, key)
}
let toProxy = new WeakMap() // 存储 源对象 -> 代理对象
let toRaw = new WeakMap() // 储存 代理对象 -> 源对象
let effectStacks = [] // 储存 effect
function reactive(target) {
// 创建代理对象 并返回
let proxy = creatReactiveObject(target)
return proxy
}
function creatReactiveObject(target) {
if (!isObject(target)) return target
// 判断是否是代理过的对象,或者是代理对象
let proxy = toProxy.get(target)
if (proxy) return proxy
if (toRaw.has(target)) return target
let handler = {
get(target, key, receiver) {
let res = Reflect.get(target, key, receiver)
track(target, key)
// 递归,如果res是对象 则继续代理
return reactive(res)
},
set(target, key, value, receiver) {
// 无效修改,直接返回
if (hasOwn(target, key) && target[key] === value) return false
let res = Reflect.set(target, key, value, receiver)
trigger(target,key)
return res
},
deleteProperty(target, key) {
let res = Reflect.deleteProperty(target, key)
console.log('deleteProperty');
return res
}
}
let observed = new Proxy(target, handler)
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
/*
{
target: {
key: new set
}
}
*/
let targetsMap = new WeakMap() // targetsMap 类似上述结构
// 动态收集依赖
function track(target, key) {
let effect = effectStacks[effectStacks.length - 1]
if(effect) {
let depsMap = targetsMap.get(target)
if(!depsMap) targetsMap.set(target, depsMap = new Map)
let deps = depsMap.get(key)
if(!deps) depsMap.set(key, deps = new Set)
if(!deps.has(effect)) deps.add(effect)
}
}
// 触发更新
function trigger(target, key) {
let depsMap = targetsMap.get(target)
if(depsMap) {
let deps = depsMap.get(key)
if(deps) {
deps.forEach(effect => {
effect()
})
}
}
}
function effect(fn) {
let effect = createEffect(fn)
// 第一次 立即执行
effect()
}
function createEffect(fn) {
let effect = function () {
run(effect, fn)
}
return effect
}
function run(effect, fn) {
// 1.将effect 加入栈中,以便依赖收集
effectStacks.push(effect)
// 2 执行fn,进行依赖收集
fn()
// 3 收集完毕,出栈
effectStacks.pop()
}
let obj = {
name: 'xx',
deepObj: {
a: 1
},
arr: [1, 2, 3]
}
let proxy = reactive(obj)
effect(() => {
console.log(proxy.name);
})
effect(() => {
console.log(proxy.name);
})
proxy.name = 'yy'