第201集:Vue 3响应式系统源码深度解析

概述

在本集中,我们将深入剖析Vue 3响应式系统的源码实现,理解其核心原理和设计思想。Vue 3响应式系统是基于ES6 Proxy实现的,相比Vue 2的Object.defineProperty有了显著的改进和优化。

响应式系统核心架构

Vue 3响应式系统主要包含以下核心模块:

  1. 响应式核心:基于Proxy的响应式代理实现
  2. 依赖收集:Track(追踪)机制
  3. 依赖触发:Trigger(触发)机制
  4. 副作用处理:Effect(副作用)系统
  5. 响应式工具:ref、reactive、computed、watch等API

源码目录结构

Vue 3响应式系统的源码主要位于packages/reactivity/src/目录下:

packages/reactivity/src/
├── index.ts              # 入口文件,导出所有API
├── reactive.ts           # 响应式核心实现
├── ref.ts                # Ref实现
├── computed.ts           # 计算属性实现
├── watcher.ts            # 观察者实现
├── effect.ts             # 副作用系统
├── track.ts              # 依赖收集
├── trigger.ts            # 依赖触发
├── baseHandlers.ts       # 基础处理器
├── collectionHandlers.ts # 集合类型处理器
├── utils.ts              # 工具函数
└── constants.ts          # 常量定义

核心源码解析

1. 响应式核心:Proxy实现

让我们首先看一下reactive.ts中的核心实现:

// packages/reactivity/src/reactive.ts
import { mutableHandlers } from './baseHandlers'
import { isObject } from './utils'

// 存储响应式对象的WeakMap,避免重复代理
const reactiveMap = new WeakMap<any, any>()

/**
 * 创建响应式对象
 * @param target 要代理的目标对象
 * @returns 响应式代理对象
 */
export function reactive(target: object) {
  // 如果不是对象或者是只读对象,直接返回
  if (!isObject(target) || (target as any)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  
  // 如果已经是响应式对象,直接返回
  if ((target as any)[ReactiveFlags.IS_REACTIVE]) {
    return target
  }
  
  // 检查是否已经有代理对象,有则直接返回
  const existingProxy = reactiveMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  
  // 创建代理对象
  const proxy = new Proxy(target, mutableHandlers)
  
  // 缓存代理对象
  reactiveMap.set(target, proxy)
  return proxy
}

2. 基础处理器:mutableHandlers

baseHandlers.ts中定义了Proxy的各种处理器,包括get、set、deleteProperty等:

// packages/reactivity/src/baseHandlers.ts
import { track, trigger } from './effect'
import { reactive, toRaw } from './reactive'
import { isObject } from './utils'

// 拦截get操作
export const get = createGetter()

// 拦截set操作
export const set = createSetter()

// 拦截delete操作
export const deleteProperty = createDeleteProperty()

// 创建get处理器
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: object, key: string | symbol, receiver: object) {
    // 处理特殊key,如__v_isReactive
    const res = Reflect.get(target, key, receiver)
    
    // 非只读情况下,进行依赖收集
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    
    // 如果是对象,递归创建响应式代理
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res)
    }
    
    return res
  }
}

// 创建set处理器
function createSetter(shallow = false) {
  return function set(target: object, key: string | symbol, value: unknown, receiver: object): boolean {
    const oldValue = (target as any)[key]
    const hadKey = hasOwn(target, key)
    
    // 执行set操作
    const result = Reflect.set(target, key, value, receiver)
    
    // 如果目标对象是响应式对象的原型,不触发更新
    if (target === toRaw(receiver)) {
      // 新增属性
      if (!hadKey) {
        trigger(target, TriggerOpTypes.ADD, key, value)
      }
      // 修改属性
      else if (hasChanged(value, oldValue)) {
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    
    return result
  }
}

3. 依赖收集:Track机制

effect.ts中的track函数负责依赖收集:

// packages/reactivity/src/effect.ts
import { activeEffect, ReactiveEffect } from './effect'

/**
 * 依赖收集
 * @param target 目标对象
 * @param type 操作类型
 * @param key 属性键
 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
  // 如果没有活跃的副作用或者不是追踪模式,直接返回
  if (!shouldTrack || activeEffect === undefined) {
    return
  }
  
  // 获取目标对象的依赖映射
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  
  // 获取属性的依赖集合
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = createDep()))
  }
  
  // 将当前活跃的副作用添加到依赖集合中
  trackEffects(dep)
}

/**
 * 追踪副作用
 * @param dep 依赖集合
 */
export function trackEffects(dep: Dep) {
  // 如果当前副作用已经在依赖集合中,直接返回
  if (dep.has(activeEffect!)) {
    return
  }
  
  // 添加副作用到依赖集合
  dep.add(activeEffect!)
  
  // 副作用添加依赖集合
  activeEffect!.deps.push(dep)
}

4. 依赖触发:Trigger机制

trigger函数负责触发依赖更新:

// packages/reactivity/src/effect.ts
/**
 * 依赖触发
 * @param target 目标对象
 * @param type 操作类型
 * @param key 属性键
 * @param newValue 新值
 * @param oldValue 旧值
 * @param oldTarget 旧目标
 */
export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  // 获取目标对象的依赖映射
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    // 没有依赖,直接返回
    return
  }
  
  // 收集需要触发的副作用
  const effects = new Set<ReactiveEffect>()
  const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
    if (effectsToAdd) {
      effectsToAdd.forEach(effect => {
        if (effect !== activeEffect || effect.allowRecurse) {
          effects.add(effect)
        }
      })
    }
  }
  
  // 根据操作类型处理不同的依赖
  if (type === TriggerOpTypes.CLEAR) {
    // 清除操作,触发所有依赖
    depsMap.forEach(add)
  } else if (key === 'length' && isArray(target)) {
    // 数组长度变化,处理特殊情况
    depsMap.forEach((dep, key) => {
      if (key === 'length' || key >= (newValue as number)) {
        add(dep)
      }
    })
  } else {
    // 处理单个属性
    if (key !== void 0) {
      add(depsMap.get(key))
    }
    
    // 根据操作类型处理其他依赖
    switch (type) {
      case TriggerOpTypes.ADD:
        if (!isArray(target)) {
          add(depsMap.get(ITERATE_KEY))
        } else if (isIntegerKey(key)) {
          // 数组新增元素,触发length依赖
          add(depsMap.get('length'))
        }
        break
      case TriggerOpTypes.DELETE:
        if (!isArray(target)) {
          add(depsMap.get(ITERATE_KEY))
        }
        break
      case TriggerOpTypes.SET:
        // 修改操作,对于Map类型需要特殊处理
        if (isMap(target)) {
          add(depsMap.get(ITERATE_KEY))
        }
        break
    }
  }
  
  // 触发所有收集到的副作用
  const run = (effect: ReactiveEffect) => {
    scheduleRun(effect)
  }
  effects.forEach(run)
}

5. 副作用系统:Effect实现

Effect是响应式系统的核心,负责管理副作用函数:

// packages/reactivity/src/effect.ts
/**
 * 副作用类
 */
export class ReactiveEffect<T = any> {
  // 副作用函数
  fn: () => T
  // 依赖数组
  deps: Dep[] = []
  // 是否允许递归
  allowRecurse?: boolean
  // 调度器
  scheduler?: EffectScheduler
  // 是否激活
  active = true
  // 父副作用
  parent: ReactiveEffect | undefined = undefined
  
  constructor(
    fn: () => T,
    scheduler?: EffectScheduler,
    scope?: EffectScope | null
  ) {
    this.fn = fn
    this.scheduler = scheduler
    recordEffectScope(this, scope)
  }
  
  /**
   * 运行副作用
   */
  run() {
    // 如果副作用未激活,直接执行函数
    if (!this.active) {
      return this.fn()
    }
    
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack
    
    // 处理嵌套副作用
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    
    try {
      // 设置父副作用
      this.parent = activeEffect
      // 激活副作用
      activeEffect = this
      // 开启追踪
      shouldTrack = true
      
      // 清理之前的依赖
      cleanupEffect(this)
      
      // 执行副作用函数
      return this.fn()
    } finally {
      // 恢复之前的状态
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined
    }
  }
  
  /**
   * 停止副作用
   */
  stop() {
    if (this.active) {
      // 清理依赖
      cleanupEffect(this)
      // 标记为非激活
      this.active = false
    }
  }
}

/**
 * 创建副作用
 * @param fn 副作用函数
 * @param options 选项
 * @returns 副作用实例
 */
export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions
): ReactiveEffectRunner {
  // 如果已经是副作用函数,直接使用其原始函数
  if ((fn as ReactiveEffectRunner).effect) {
    fn = (fn as ReactiveEffectRunner).effect!.fn
  }
  
  // 创建副作用实例
  const _effect = new ReactiveEffect(fn)
  
  // 应用选项
  if (options) {
    extend(_effect, options)
    if (options.scope) {
      recordEffectScope(_effect, options.scope)
    }
  }
  
  // 运行副作用
  _effect.run()
  
  // 返回runner函数
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}

Ref实现源码

ref.ts实现了Ref API,用于处理基本类型的响应式:

// packages/reactivity/src/ref.ts
/**
 * Ref类型接口
 */
export interface Ref<T = any> {
  value: T
}

/**
 * 创建Ref
 * @param value 初始值
 * @returns Ref对象
 */
export function ref<T extends object>(value: T): ToRef<T>
export function ref<T>(value: T): Ref<T>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
  return createRef(value, false)
}

/**
 * 创建Ref实例
 * @param rawValue 原始值
 * @param shallow 是否浅层
 * @returns Ref对象
 */
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

/**
 * Ref实现类
 */
class RefImpl<T> {
  // 依赖集合
  private _value: T
  private _rawValue: T
  public dep?: Dep = undefined
  public readonly __v_isRef = true
  
  constructor(value: T, public readonly __v_isShallow: boolean) {
    // 保存原始值
    this._rawValue = __v_isShallow ? value : toRaw(value)
    // 转换值(如果是对象,创建响应式代理)
    this._value = __v_isShallow ? value : convert(value)
  }
  
  /**
   * 获取值,触发依赖收集
   */
  get value() {
    trackRefValue(this)
    return this._value
  }
  
  /**
   * 设置值,触发依赖更新
   */
  set value(newVal) {
    const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    
    // 如果值发生变化
    if (hasChanged(newVal, this._rawValue)) {
      // 更新原始值
      this._rawValue = newVal
      // 更新值
      this._value = useDirectValue ? newVal : convert(newVal)
      // 触发依赖
      triggerRefValue(this, newVal)
    }
  }
}

Computed实现源码

computed.ts实现了计算属性,具有缓存机制:

// packages/reactivity/src/computed.ts
/**
 * 创建计算属性
 * @param getterOrOptions getter函数或选项对象
 * @returns 计算属性Ref
 */
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
): ComputedRef<T> {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  
  // 处理选项
  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = () => {
      console.warn('Write operation failed: computed value is readonly')
    }
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  
  // 创建计算属性实例
  return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set)
}

/**
 * 计算属性实现类
 */
class ComputedRefImpl<T> {
  private _value!: T
  private _dirty = true
  
  public dep?: Dep = undefined
  public readonly effect: ReactiveEffect<T>
  
  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean
  
  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    // 创建副作用,用于追踪依赖
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        // 依赖变化,触发更新
        triggerRefValue(this)
      }
    })
    this.effect.computed = this
    this.effect.active = true
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }
  
  /**
   * 获取计算属性值
   */
  get value() {
    // 触发依赖收集
    trackRefValue(this)
    // 如果值脏了,重新计算
    if (this._dirty) {
      this._dirty = false
      this._value = this.effect.run()!
    }
    return this._value
  }
  
  /**
   * 设置计算属性值
   */
  set value(newValue: T) {
    this._setter(newValue)
  }
}

响应式系统工作流程

现在我们可以总结Vue 3响应式系统的完整工作流程:

  1. 创建响应式对象:通过reactiveref创建响应式代理
  2. 依赖收集
    • 当访问响应式对象的属性时,触发get处理器
    • get处理器调用track函数进行依赖收集
    • track函数将当前活跃的副作用添加到依赖集合中
  3. 依赖触发
    • 当修改响应式对象的属性时,触发set处理器
    • set处理器调用trigger函数触发依赖更新
    • trigger函数遍历并执行所有相关的副作用
  4. 副作用执行
    • 副作用函数执行,可能会访问其他响应式对象
    • 重复依赖收集过程,形成依赖关系网

性能优化

Vue 3响应式系统相比Vue 2有以下性能优化:

  1. 基于Proxy:可以拦截所有对象操作,包括新增属性、删除属性等
  2. 惰性依赖收集:只收集实际使用的属性依赖
  3. WeakMap缓存:避免重复创建响应式代理
  4. 副作用清理机制:自动清理不再使用的依赖
  5. 更精确的依赖触发:只触发相关的副作用,避免不必要的更新

实际应用场景

了解响应式系统源码可以帮助我们:

  1. 更好地使用响应式API:理解API的设计意图和使用场景
  2. 排查响应式相关问题:快速定位和解决响应式数据不更新等问题
  3. 优化性能:根据响应式系统的工作原理,写出更高效的代码
  4. 扩展响应式系统:自定义响应式行为,满足特殊需求

总结

本集深入剖析了Vue 3响应式系统的源码实现,从核心架构到具体模块,详细讲解了其工作原理和设计思想。理解响应式系统的源码对于掌握Vue 3至关重要,可以帮助我们更好地使用Vue 3,写出高效、可靠的代码。

在后续的源码解析系列中,我们将继续深入探讨Vue 3的其他核心模块,包括编译原理、虚拟DOM、组件系统等。

« 上一篇 201-vue3-reactive-system-source 下一篇 » Vue 3 编译原理与模板编译:从模板到渲染函数的转换