Vue 3 响应式精简化解析

150 阅读1分钟

前置知识:

精简后的处理

  1. 只考虑普通对象和 reactive()
  2. 不考虑各种边界条件和判断,详细请看源码

数据结构

flowchart TD
	rawObject ==>|"reactive(rawObject)"| reactiveObject
  dep --> effect
  deps --> dep
  subgraph effect
  	deps["deps: dep[]"]
  end
	subgraph targetMap
		reactiveObject -.-> depsMap
    subgraph reactiveObject
      get --> track
      set --> trigger
    end
    subgraph depsMap
    	key -.-> dep["dep: effect Set"]
    end
	end

reactive

  1. 拦截 get 执行 track --> 依赖收集
  2. 拦截 set 执行 trigger --> 执行【副作用函数】
function reactive(rawObject) {
    return new Proxy(rawObject, {
        get(target, key) {
            const res = Reflect.get(target, key)
            
            // ⭐️ 依赖收集 ⭐️
            track(target, key)
            
            // 获取的值是对象,则返回前要做响应式处理
            return typeof res === 'object' ? reactive(res) : res
        },
        set(target, key, value) {
            const res = Reflect.set(target, key, value)
            
            // ⭐️ 触发副作用 ⭐️
            trigger(target, key)
            
            return res
        },
    })
}

一些全局变量

// 当前正在执行的 effect 函数
let activeEffect
// 保存所有【响应式对象】和【其所有属性值对应的副作用】的映射
const targetMap = new Map()

createReactiveEffect

将一个【普通函数】封装成一个【副作用函数】

可以简单的认为:

  • 【副作用函数】就是组件实例的【更新函数】
  • 【更新函数】就是由组件的【渲染函数】经过副作用包装得到:
    • instance.update = createReactiveEffect(instance.render)
/**
 * @param fn 一个函数,函数体里面使用到了【响应式对象】
 */
function createReactiveEffect(fn) {
    const effect = () => {
        
        // 清除依赖
        effect.deps.forEach((dep) => {
            dep.delete(effect)
        })
    
        try {
            // 记录当前 effect,track 要用
            activeEffect = effect
            // ⭐️ 执行 fn() 的时候,会访问到其中的【响应式对象】,也就触发 get,最终触发了 track ⭐️
            return fn()
        } finally { // 即使前面 return,最后也必定执行
            activeEffect = undefined
        }
    }
    
    // 用来保存依赖 dep,作用就是前面的清除依赖
    effect.deps = []
    
    return effect
}

track

依赖收集,双向绑定

function track(target, key) {
    // targetMap: target --> depsMap
    let depsMap = targetMap.get(target)
    if (!depsMap) {  // 初始化
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    
    // depsMap: key --> dep
    let dep = depsMap.get(key)
    if (!dep) {  // 初始化
        dep = new Set()
        depsMap.set(key, dep)
    }
    
    // 双向绑定
    if (!dep.has(activeEffect)) {
        dep.add(activeEffect)
        activeEffect.deps.push(dep)
    }
}

trigger

执行对应的【副作用函数】

function trigger(target, key) {
    const depsMap = targetMap.get(target)
    const dep = depsMap.get(key)
    
    for (const effect of dep) {
        effect()
    }
}