let activeEffect
const effectStack = []
// 副作用函数栈
function 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 = []
// 只有在不为lazy的时候立即执行
if (!options.lazy) {
effectFn()
}
return effectFn
}
// 清除副作用函数避免不必要的更新
function cleanup(effectFn) {
// console.warn('cleanup', [...effectFn.deps])
// 遍历依赖数组
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]
// 从依赖集合中删除该函数
deps.delete(effectFn)
}
// 重置数组
effectFn.deps.length = 0
}
function track(target, key) {
if (!activeEffect) {
return
}
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)
// console.log('getter track', key, deps)
// deps 是一个与当前副作用函数关联的 Set
// 添加到函数的 deps 数组中
activeEffect.deps.push(deps)
}
function trigger(target, key) {
const depsMap = bucket.get(target)
if (!depsMap) {
return
}
const effects = depsMap.get(key)
const effectToRun = new Set()
effects && effects.forEach(effectFn=>{
if (effectFn !== activeEffect) {
effectToRun.add(effectFn)
}
}
)
effectToRun.forEach(effectFn=>{
// 如果有调度器,则执行调度器
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn)
} else {
effectFn()
}
}
)
}
const bucket = new WeakMap()
const data = {
foo: 1,
bar: 1,
}
const obj = new Proxy(data,{
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key)
}
})
// 封装为computed函数
function computed(getter) {
// 用来缓存上一次的值
let value
// 用于标记是否需要重新取值
let dirty = true
const effectFn = effect(getter, {
// 让副作用函数不要立即执行
lazy: true,
scheduler() {
// 数据变化,执行调度器,让dirty为true
dirty = true
// 当计算属性依赖的响应式数据变化时,手动调用trigger函数出发响应
trigger(obj, 'value')
}
})
const obj = {
// getter 在取值时会自动执行
get value() {
// 只有当dirty为true的时候,才进行取值操作
if (dirty) {
value = effectFn()
// 将dirty设置为false,下次直接使用缓存的值
dirty = false
}
// 当读取value时,手动调用track函数进行追踪
track(obj, 'value')
return value
}
}
return obj
}
const sumRes = computed(()=>{
console.log('effect run')
return obj.foo + obj.bar
}
)
effect(() => {
console.log('effect sumRes', sumRes.value)
})
obj.foo++