手摸手学习vue3源码七:effect - reactive 的核心

78 阅读1分钟

文件目录

packages/reactivity/src/effect.ts

作用

依赖收集/更新

源码解读

// 入口文件
function effect(fn, options) {
    // 如果已经是 effect,重置为原始fn
    if (fn.effect instanceof ReactiveEffect) {
        fn = fn.effect.fn
    }
    
    // 通过类 ReactiveEffect 创建 effect
    const e = new ReactEffect(fn)
    
    // 把 fn 浅拷贝到 effect 上
    if(options) {
        extend(e, fn)
    }
    
    // 直接执行一次 effect
    try{ 
        e.run()
    } catch(err) {
        e.stop()
        throw err
    }
    
    
}
class ReactiveEffect {
    
    // 定义
    deps? = undefined
    depsTail? = undefined
    flags = EffectFlags.ACTIVE | EffectFlags.TRACKING
    nextEffect? = undefined
    cleanup?() => void = undefined
    scheduler? = undefined
    onStop?: () => void
    onTrack?: () => void
    onTrigger?: () => void
    
    
    
    
    constructor(fn) {
        // 记录 该副作用所属的作用域
        if (activeEffectScope && activeEffectScope.active) {
          activeEffectScope.effects.push(this)
        }
    }
    
    pause() {}
    resume() {}
    run() {
        // 没有激活,可以判断是调用了 stop 函数
        if (!(this.flags & EffectFlags.ACTIVE)) {
            // 直接执行 fn
            return this.fn()
        }
        
        // 已激活effect 记录状态
        // |= 按位或赋值运算符
        this.flags |= EffectFlags.RUNNING
        
        // 清除依赖
        cleanupEffect(this)
        // 准备深度跟踪
        prepareDeps(this)
        
        // 这一步骤,更方便和快速的操作 effect , 当前要操作的 effect 进行缓存,执行之后清空
        
        // 当前激活 effect 和 栈中的 effect 分别进行存储为 prevEffect 和 preShouldTrack
        // 后,重新赋值
        const prevEffect = activeSub
        const prevShouldTrack = shouldTrack
        activeSub = this
        shouldTrack = true
        
        // 运行 fn
        try {
            return this.fn()
        } finally {
            // 深度清理 effect
            cleanupDeps(this)
            
            // 重置 activeSub shouldTrack 将运行前缓存的 effect 重新赋值
            activeSub = prevEffect
            shouldTrack = prevShouldTrack
            // 标记依赖变化
            // &= 按位与赋值符
            this.flag &= ~EffectFlags.RUNNING
        }
        
        
    }
    strop() {}
    trigger() {}
}

cleanupEffect

  • 把当前激活 effect 赋值给 前一个
  • 清理当前激活 effect
function cleanupEffect(e) {
    const { cleanup } = e
    e.cleanup = undefined
    if (cleanup) {
        const prevSub = activeSub
        activeSub = undefined
        try {
            cleanup()
        } finally {
            activeSub = prevSub
        }
    }
}

prepareDeps

function prepareDeps(sub) {
    for( let link = sub.deps; link; link = link.nextDep) {
        link.version = -1
        link.preActiveLink = link.dep.activeLink
        link.dep.activeLink = link
    }
}

cleanupDeps

  • 清理没有使用的深度
function cleanupDeps(sub) {
    let head
    let tail = sub.depsTail
    for(let link = tail; link; link = link.prevDep) {
        if (link.version === -1) {
            if (link === tail) tail = link.prevDep
            // 未使用-将其从深度的订阅效果列表中删除
            removeSub(link)
            // 同时把它从这个效果的深度列表中移除
            removeDep(link)
        } else {
            head = link
        }
        
        link.dep.activeLink = link.prevActiveLink
        link.prevActiveLink = undefined
    }
    
    sub.deps = head
    sub.depsTail = tail
}