参照源码
let toProxy = new WeakMap()
let toRaw = new WeakMap()
const baseHander = {
get(target, key){
const res = Reflect.get(target, key)
track(target, key)
return typeof res=='object' ? reactive(res) : res
},
set(target, key, val){
const info = {oldValue: target[key], newValue:val}
const res = Reflect.set(target, key, val)
trigger(target, key, info)
return res
}
}
function reactive(target){
let observed = toProxy.get(target)
if(observed){
return observed
}
if(toRaw.get(target)){
return target
}
observed = new Proxy(target, baseHander)
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
let effectStack = []
let tagetMap = new WeakMap()
function trigger(target, key, info){
const depsMap = tagetMap.get(target)
if(depsMap===undefined){
return
}
const effects = new Set()
const computedRunners = new Set()
if(key){
let deps = depsMap.get(key)
deps.forEach(effect=>{
if(effect.computed){
computedRunners.add(effect)
}else{
effects.add(effect)
}
})
}
effects.forEach(effect=> effect())
computedRunners.forEach(effect=> effect())
}
function track(target, key){
let effect = effectStack[effectStack.length-1]
if(effect){
let depsMap = tagetMap.get(target)
if(depsMap===undefined){
depsMap = new Map()
tagetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if(dep===undefined){
dep = new Set()
depsMap.set(key, dep)
}
if(!dep.has(effect)){
dep.add(effect)
effect.deps.push(dep)
}
}
}
function effect(fn,options={}){
let e = createReactiveEffect(fn, options)
if(!options.lazy){
e()
}
return e
}
function createReactiveEffect(fn,options){
const effect = function effect(...args){
return run( effect, fn , args)
}
effect.deps = []
effect.computed = options.computed
effect.lazy = options.lazy
return effect
}
function run(effect, fn , args){
if(effectStack.indexOf(effect)===-1){
try{
effectStack.push(effect)
return fn(...args)
}
finally{
effectStack.pop()
}
}
}
function computed(fn){
const runner = effect(fn,{computed:true, lazy:true})
return {
effect:runner,
get value(){
return runner()
}
}
}
简单实现-手写
let currentEffect;
class Dep {
constructor() {
this.effects = new Set()
}
depend() {
if(currentEffect){
this.effects.add(currentEffect)
}
}
notice() {
this.effects.forEach(effect=>{
effect()
})
}
}
let targetMap = new Map()
function getDep(target,key){
let depsMap = targetMap.get(target)
if(!depsMap){
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if(!dep){
dep = new Dep()
depsMap.set(key,dep)
}
return dep
}
function reactivity(obj) {
return new Proxy(obj,{
get(target,key){
const dep = getDep(target,key)
dep.depend()
return Reflect.get(target,key)
},
set(target,key,newVal){
const dep = getDep(target,key)
const ret = Reflect.set(target,key,newVal)
dep.notice()
return ret
}
})
}
function effectWatch(effect) {
currentEffect = effect
effect()
currentEffect = null
}
let obj = reactivity({
count:1
})
let b
effectWatch(()=>{
b = obj.count + 1
console.log(b)
})
obj.count = 2
obj.count = 3