Vue3 的响应式系统源码级执行流程解析
字数 577 2025-11-13 18:24:01

Vue3 的响应式系统源码级执行流程解析

描述:Vue3 的响应式系统基于 Proxy 实现,当数据被访问时自动收集依赖,数据被修改时自动触发更新。我们将深入源码级别的执行流程,从 reactive 函数创建代理对象开始,到 track 依赖收集、trigger 触发更新的完整过程。

解题过程

  1. reactive 函数创建响应式对象

    • 当调用 reactive(target) 时,首先检查目标对象是否已经是响应式对象(通过 ReactiveFlags.IS_REACTIVE 标记)
    • 如果不是,则调用 createReactiveObject() 函数:
    function createReactiveObject(target) {
      // 1. 检查是否只读对象或非对象类型
      if (!isObject(target)) return target
    
      // 2. 如果已经是响应式对象,直接返回
      if (target[ReactiveFlags.IS_REACTIVE]) return target
    
      // 3. 检查是否已有存在的代理对象(避免重复代理)
      const existingProxy = proxyMap.get(target)
      if (existingProxy) return existingProxy
    
      // 4. 创建 Proxy 代理对象
      const proxy = new Proxy(
        target,
        baseHandlers  // 使用基础的 handlers(mutableHandlers)
      )
    
      // 5. 缓存代理对象到 WeakMap 中
      proxyMap.set(target, proxy)
      return proxy
    }
    
  2. Proxy handlers 的 get 陷阱实现

    • 当访问响应式对象属性时,触发 get 陷阱:
    const mutableHandlers = {
      get(target, key, receiver) {
        // 1. 检查特殊标记(如 __v_isReactive)
        if (key === ReactiveFlags.IS_REACTIVE) return true
    
        // 2. 执行原始获取操作
        const res = Reflect.get(target, key, receiver)
    
        // 3. 依赖收集(关键步骤)
        track(target, key)
    
        // 4. 如果值是对象,递归转换为响应式
        if (isObject(res)) {
          return reactive(res)
        }
    
        return res
      }
    }
    
  3. track 依赖收集详细流程

    • track 函数建立数据与副作用函数(effect)的关联:
    function track(target, key) {
      // 1. 检查是否应该收集依赖(有活跃的 effect)
      if (!activeEffect || !shouldTrack) return
    
      // 2. 获取 target 对应的依赖映射(depsMap)
      let depsMap = targetMap.get(target)
      if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()))
      }
    
      // 3. 获取 key 对应的依赖集合(dep)
      let dep = depsMap.get(key)
      if (!dep) {
        depsMap.set(key, (dep = new Set()))
      }
    
      // 4. 将当前活跃的 effect 添加到依赖集合
      if (!dep.has(activeEffect)) {
        dep.add(activeEffect)
        // 同时 effect 也记录自己属于哪些 dep(用于清理)
        activeEffect.deps.push(dep)
      }
    }
    
  4. Proxy handlers 的 set 陷阱实现

    • 当修改响应式对象属性时,触发 set 陷阱:
    const mutableHandlers = {
      set(target, key, value, receiver) {
        // 1. 检查是否是新增属性还是修改已有属性
        const hadKey = hasOwn(target, key)
        const oldValue = target[key]
    
        // 2. 执行原始设置操作
        const result = Reflect.set(target, key, value, receiver)
    
        // 3. 只有当目标对象就是 receiver 的代理目标时才触发更新
        if (target === toRaw(receiver)) {
          if (!hadKey) {
            // 新增属性触发 ADD 类型的更新
            trigger(target, key, TriggerOpTypes.ADD, value)
          } else if (hasChanged(value, oldValue)) {
            // 修改属性且值发生变化触发 SET 类型的更新
            trigger(target, key, TriggerOpTypes.SET, value, oldValue)
          }
        }
    
        return result
      }
    }
    
  5. trigger 触发更新详细流程

    • trigger 函数根据修改类型执行相应的更新操作:
    function trigger(target, key, type, newValue, oldValue) {
      // 1. 获取 target 对应的依赖映射
      const depsMap = targetMap.get(target)
      if (!depsMap) return
    
      // 2. 创建要执行的 effects 集合
      const effects = new Set()
    
      // 3. 添加 key 对应的 effects
      const add = (effectsToAdd) => {
        if (effectsToAdd) {
          effectsToAdd.forEach(effect => {
            effects.add(effect)
          })
        }
      }
    
      // 4. 根据操作类型收集需要触发的 effects
      if (key !== void 0) {
        add(depsMap.get(key))
      }
    
      // 5. 处理数组长度变化或新增索引的情况
      if (type === TriggerOpTypes.ADD && isArray(target)) {
        add(depsMap.get('length'))
      }
    
      // 6. 执行所有收集到的 effects
      const run = (effect) => {
        if (effect.options.scheduler) {
          effect.options.scheduler(effect)  // 使用调度器
        } else {
          effect()  // 直接执行
        }
      }
    
      effects.forEach(run)
    }
    
  6. effect 副作用函数的执行机制

    • effect 函数创建响应式副作用:
    function effect(fn, options = {}) {
      // 1. 创建 ReactiveEffect 实例
      const effect = createReactiveEffect(fn, options)
    
      // 2. 如果不是懒加载,立即执行一次
      if (!options.lazy) {
        effect()
      }
    
      return effect
    }
    
    function createReactiveEffect(fn, options) {
      const effect = function reactiveEffect() {
        // 1. 清理 effect 与之前依赖的关联
        cleanup(effect)
    
        // 2. 设置当前活跃的 effect
        activeEffect = effect
    
        // 3. 执行原始函数(期间会触发 track)
        return fn()
      }
    
      // 4. 附加属性
      effect.deps = []  // 存储该 effect 依赖的所有 dep 集合
      effect.options = options
    
      return effect
    }
    

这个完整的执行流程确保了 Vue3 响应式系统的高效运行,通过 Proxy 拦截、依赖收集和精准触发更新,实现了数据的自动响应。

Vue3 的响应式系统源码级执行流程解析 描述 :Vue3 的响应式系统基于 Proxy 实现,当数据被访问时自动收集依赖,数据被修改时自动触发更新。我们将深入源码级别的执行流程,从 reactive 函数创建代理对象开始,到 track 依赖收集、trigger 触发更新的完整过程。 解题过程 : reactive 函数创建响应式对象 当调用 reactive(target) 时,首先检查目标对象是否已经是响应式对象(通过 ReactiveFlags.IS_REACTIVE 标记) 如果不是,则调用 createReactiveObject() 函数: Proxy handlers 的 get 陷阱实现 当访问响应式对象属性时,触发 get 陷阱: track 依赖收集详细流程 track 函数建立数据与副作用函数(effect)的关联: Proxy handlers 的 set 陷阱实现 当修改响应式对象属性时,触发 set 陷阱: trigger 触发更新详细流程 trigger 函数根据修改类型执行相应的更新操作: effect 副作用函数的执行机制 effect 函数创建响应式副作用: 这个完整的执行流程确保了 Vue3 响应式系统的高效运行,通过 Proxy 拦截、依赖收集和精准触发更新,实现了数据的自动响应。