Vue3 响应式系统原理

深入解析 Vue3 基于 Proxy 的响应式系统,从 reactive/ref 的实现到 track/trigger 机制,从 ReactiveEffect 到 Computed 原理,全面理解 Vue3 响应式的设计精髓。


一、前言

Vue3 的响应式系统是一次彻底的架构重构。从 Object.definePropertyProxy,从 Dep/Watchertrack/trigger + ReactiveEffect,不仅解决了 Vue2 的诸多局限,还带来了更好的性能和更灵活的扩展能力。

本章将深入 @vue/reactivity 包的源码,剖析 Vue3 响应式系统的完整实现。


二、Proxy 替代 Object.defineProperty

2.1 为什么 Vue3 选择 Proxy

Vue2 的 Object.defineProperty 存在根本性局限,而 Proxy 作为 ES6 引入的元编程特性,可以完美解决这些问题:

能力 Object.defineProperty Proxy
新增属性 不支持 支持
删除属性 不支持 支持
数组索引 不支持 支持
数组 length 不支持 支持
in 操作符 不支持 支持
Object.keys() 不支持 支持
Map/Set/WeakMap 不支持 支持
嵌套对象 需递归遍历 懒代理(访问时才代理)

2.2 Proxy 基础用法

const target = { name: 'Vue', count: 0 }

const proxy = new Proxy(target, {
  get(target, key, receiver) {
    console.log('读取:', key)
    // receiver 保证正确的 this 指向
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver) {
    console.log('设置:', key, '=', value)
    return Reflect.set(target, key, value, receiver)
  },
  has(target, key) {
    console.log('in 操作:', key)
    return Reflect.has(target, key)
  },
  deleteProperty(target, key) {
    console.log('删除:', key)
    return Reflect.deleteProperty(target, key)
  },
  ownKeys(target) {
    console.log('遍历 keys')
    return Reflect.ownKeys(target)
  }
})

proxy.name        // 读取: name
proxy.count = 1   // 设置: count = 1
'name' in proxy   // in 操作: name
Object.keys(proxy) // 遍历 keys
delete proxy.name // 删除: name

2.3 Vue3 中的 createReactiveObject

// Vue3 源码:packages/reactivity/src/reactive.ts
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  // 非对象类型不处理
  if (!isObject(target)) {
    return target
  }

  // 已经是代理对象,直接返回
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }

  // 目标对象已经是代理(避免重复代理)
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }

  // 创建 Proxy
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )

  // 缓存代理对象
  proxyMap.set(target, proxy)
  return proxy
}

三、reactive 与 ref 的实现

3.1 reactive 的实现

// Vue3 源码:packages/reactivity/src/reactive.ts
export function reactive(target) {
  return createReactiveObject(
    target,
    false,
    mutableHandlers,      // 普通对象的 handler
    mutableCollectionHandlers, // Map/Set/WeakMap/WeakSet 的 handler
    reactiveMap
  )
}

// mutableHandlers 的核心实现
const mutableHandlers = {
  get(target, key, receiver) {
    // 特殊 key 的处理
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    }
    if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    }
    if (key === ReactiveFlags.RAW) {
      return target
    }

    // 对数组特殊方法的处理(includes, indexOf, lastIndexOf)
    const targetIsArray = isArray(target)
    if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }

    const res = Reflect.get(target, key, receiver)

    // 依赖收集
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }

    // 懒代理:访问时才递归代理
    if (isShallow) {
      return res
    }
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res)
    }
    return res
  },

  set(target, key, value, receiver) {
    const oldValue = target[key]

    // 判断是新增属性还是修改属性
    const hadKey = isArray(target)
      ? isIntegerKey(key) && key < target.length
      : hasOwn(target, key)

    const result = Reflect.set(target, key, value, receiver)

    // 触发更新
    if (!hadKey) {
      // 新增属性
      trigger(target, TriggerOpTypes.ADD, key, value)
    } else if (hasChanged(value, oldValue)) {
      // 修改属性
      trigger(target, TriggerOpTypes.SET, key, value, oldValue)
    }
    return result
  },

  deleteProperty(target, key) {
    const hadKey = hasOwn(target, key)
    const oldValue = target[key]
    const result = Reflect.deleteProperty(target, key)
    if (hadKey && result) {
      trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
    }
    return result
  },

  has(target, key) {
    const result = Reflect.has(target, key)
    track(target, TrackOpTypes.HAS, key)
    return result
  },

  ownKeys(target) {
    track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
    return Reflect.ownKeys(target)
  }
}

3.2 ref 的实现

// Vue3 源码:packages/reactivity/src/ref.ts
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 : toReactive(value)
  }

  get value() {
    // 依赖收集
    trackRefValue(this)
    return this._value
  }

  set value(newVal) {
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = this.__v_isShallow ? newVal : toReactive(newVal)
      // 触发更新
      triggerRefValue(this)
    }
  }
}

export function ref(value) {
  return createRef(value, false)
}

function createRef(rawValue, shallow) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

// trackRefValue 和 triggerRefValue
export function trackRefValue(ref) {
  if (shouldTrack && activeEffect) {
    ref = toRaw(ref)
    trackEffects(ref.dep || (ref.dep = createDep()))
  }
}

export function triggerRefValue(ref) {
  ref = toRaw(ref)
  if (ref.dep) {
    triggerEffects(ref.dep)
  }
}

四、track/trigger 机制

4.1 依赖存储数据结构

Vue3 使用三层数据结构来存储依赖关系:

具体示例

依赖存储结构

存储

存储

WeakMap
targetMap

Map
key -> dep

Set
ReactiveEffect

user对象

name -> Set

age -> Set

order对象

price -> Set

// 全局依赖存储
type Dep = Set<ReactiveEffect>
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<any, KeyToDepMap>()

4.2 track 的实现

// Vue3 源码:packages/reactivity/src/effect.ts
export function track(target, type, key) {
  if (shouldTrack && activeEffect) {
    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)
  }
}

export function trackEffects(dep) {
  let shouldTrack = false
  if (effectTrackDepth <= maxMarkerBits) {
    // 使用位运算优化去重
    if (!newTracked(dep)) {
      dep.n |= trackOpBit
      shouldTrack = !wasTracked(dep)
    }
  } else {
    shouldTrack = !dep.has(activeEffect!)
  }

  if (shouldTrack) {
    dep.add(activeEffect!)
    activeEffect.deps.push(dep)
  }
}

4.3 trigger 的实现

// Vue3 源码:packages/reactivity/src/effect.ts
export function trigger(target, type, key, newValue, oldValue) {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    return
  }

  let deps: (Dep | undefined)[] = []

  // 收集需要触发的 effect
  if (key === 'length' && isArray(target)) {
    // 数组 length 变化,需要触发所有大于新 length 的索引
    depsMap.forEach((dep, key) => {
      if (key === 'length' || key >= (newValue as number)) {
        deps.push(dep)
      }
    })
  } else {
    // 特定 key 的依赖
    if (key !== void 0) {
      deps.push(depsMap.get(key))
    }

    // 迭代相关依赖(for...in, Object.keys)
    switch (type) {
      case TriggerOpTypes.ADD:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        } else if (isIntegerKey(key)) {
          deps.push(depsMap.get('length'))
        }
        break
      case TriggerOpTypes.DELETE:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        }
        break
      case TriggerOpTypes.SET:
        if (isMap(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
        }
        break
    }
  }

  // 去重并触发
  if (deps.length === 1) {
    if (deps[0]) {
      triggerEffects(deps[0])
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    triggerEffects(createDep(effects))
  }
}

export function triggerEffects(dep) {
  const effects = isArray(dep) ? dep : [...dep]
  // 先执行计算属性的 effect(清理依赖)
  for (const effect of effects) {
    if (effect.computed) {
      triggerEffect(effect)
    }
  }
  // 再执行普通 effect
  for (const effect of effects) {
    if (!effect.computed) {
      triggerEffect(effect)
    }
  }
}

function triggerEffect(effect) {
  if (effect !== activeEffect || effect.allowRecurse) {
    if (effect.scheduler) {
      effect.scheduler()  // 使用调度器(如组件更新)
    } else {
      effect.run()        // 直接执行
    }
  }
}

五、ReactiveEffect

5.1 ReactiveEffect 的核心实现

ReactiveEffect 是 Vue3 中替代 Vue2 Watcher 的核心类:

// Vue3 源码:packages/reactivity/src/effect.ts
export class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = []
  parent: ReactiveEffect | undefined = undefined

  computed?: ComputedRefImpl<T>
  allowRecurse?: boolean
  private deferStop?: boolean

  onStop?: () => void
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void

  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope
  ) {
    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

      trackOpBit = 1 << ++effectTrackDepth

      if (effectTrackDepth <= maxMarkerBits) {
        initDepMarkers(this)
      } else {
        cleanupEffect(this)
      }

      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        finalizeDepMarkers(this)
      }

      trackOpBit = 1 << --effectTrackDepth

      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined

      if (this.deferStop) {
        this.stop()
      }
    }
  }

  stop() {
    if (activeEffect === this) {
      this.deferStop = true
    } else if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

5.2 effect 函数

export function effect(fn, options) {
  const _effect = new ReactiveEffect(fn)
  if (options) {
    extend(_effect, options)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  if (!options || !options.lazy) {
    _effect.run()
  }
  const runner = _effect.run.bind(_effect)
  runner.effect = _effect
  return runner
}

六、Computed 原理

6.1 Computed 的核心实现

// Vue3 源码:packages/reactivity/src/computed.ts
class ComputedRefImpl<T> {
  public dep?: Dep = undefined
  private _value!: T
  public readonly effect: ReactiveEffect<T>
  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean = false
  public _dirty = true

  constructor(
    getter,
    private readonly _setter,
    isReadonly: boolean
  ) {
    this.effect = new ReactiveEffect(getter, () => {
      // 调度器:当依赖变化时执行
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this)
      }
    })
    this.effect.computed = this
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    const self = toRaw(this)
    trackRefValue(self)
    if (self._dirty) {
      self._dirty = false
      self._value = self.effect.run()
    }
    return self._value
  }

  set value(newValue) {
    this._setter(newValue)
  }
}

export function computed(getterOrOptions) {
  let getter, setter
  const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    getter = getterOrOptions
    setter = () => {
      console.warn('Write operation failed: computed value is readonly')
    }
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  return new ComputedRefImpl(
    getter,
    setter,
    onlyGetter || !setter
  )
}

6.2 Computed 的缓存机制

Dep ReactiveEffect ComputedRef Dep ReactiveEffect ComputedRef 首次访问 computed.value 再次访问 computed.value 某个依赖变化 下次访问 computed.value _dirty = true? effect.run() 执行getter,访问依赖 收集依赖 建立依赖关系 返回计算结果 _dirty = false 缓存结果 _dirty = false 直接返回缓存值 触发调度器 _dirty = true triggerRefValue _dirty = true effect.run() 重新计算 _dirty = false

七、嵌套 effect 处理

7.1 嵌套 effect 的场景

在 Vue3 中,effect 可以嵌套执行。例如,组件渲染 effect 中可能包含另一个 effect:

// 外层 effect(组件渲染)
effect(() => {
  console.log('outer:', state.outer)

  // 内层 effect(可能在某个 hook 中)
  effect(() => {
    console.log('inner:', state.inner)
  })
})

7.2 Vue3 的解决方案

Vue3 使用栈结构来管理嵌套的 effect:

// 全局变量
let activeEffect: ReactiveEffect | undefined
let shouldTrack = true

class ReactiveEffect {
  parent: ReactiveEffect | undefined = undefined

  run() {
    if (!this.active) {
      return this.fn()
    }

    // 检查循环依赖
    let parent: ReactiveEffect | undefined = activeEffect
    while (parent) {
      if (parent === this) {
        return  // 避免无限循环
      }
      parent = parent.parent
    }

    try {
      // 将当前 effect 设为 active,保存父 effect
      this.parent = activeEffect
      activeEffect = this
      shouldTrack = true

      return this.fn()
    } finally {
      // 恢复父 effect
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined
    }
  }
}

7.3 嵌套 effect 的依赖收集

依赖关系

effect栈

访问

访问

访问

访问

activeEffect

外层effect
组件渲染

内层effect
某个hook

state.outer

外层effect

state.inner

内层effect

state.shared


八、依赖清理

8.1 为什么需要依赖清理

在条件渲染场景中,某些依赖可能只在特定条件下被访问。当条件变化时,旧的依赖应该被清理:

const state = reactive({ show: true, name: 'Vue' })

effect(() => {
  if (state.show) {
    console.log(state.name)  // 只在这个条件下依赖 name
  }
})

// 初始时:effect 依赖 show 和 name
state.show = false
// 现在 effect 只依赖 show,但 name 的 dep 中还保存着这个 effect
// 如果不清理,修改 name 也会触发 effect,造成不必要的更新

8.2 Vue3 的依赖清理机制

Vue3 使用位标记算法来高效清理依赖:

// 位标记相关变量
let effectTrackDepth = 0
let trackOpBit = 1
const maxMarkerBits = 30

// 每个 dep 有两个位标记字段
// dep.w: 上一次 run 时收集的 effect 的位标记
// dep.n: 当前 run 时收集的 effect 的位标记

function initDepMarkers({ deps }) {
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].w |= trackOpBit  // 标记为"旧依赖"
    }
  }
}

function finalizeDepMarkers(effect) {
  const { deps } = effect
  if (deps.length) {
    let ptr = 0
    for (let i = 0; i < deps.length; i++) {
      const dep = deps[i]
      if (wasTracked(dep) && !newTracked(dep)) {
        // 旧依赖但当前没有收集到,需要清理
        dep.delete(effect)
      } else {
        deps[ptr++] = dep
      }
      // 清除位标记
      dep.w &= ~trackOpBit
      dep.n &= ~trackOpBit
    }
    deps.length = ptr
  }
}

function wasTracked(dep) {
  return (dep.w & trackOpBit) > 0
}

function newTracked(dep) {
  return (dep.n & trackOpBit) > 0
}

8.3 依赖清理的完整流程

DepC DepB DepA ReactiveEffect DepC DepB DepA ReactiveEffect 第一次执行 第二次执行(条件变化) wasTracked=1, newTracked=1 ✅保留 wasTracked=1, newTracked=0 ❌清理 wasTracked=0, newTracked=1 ✅新增 track(A) track(B) deps = [DepA, DepB] initDepMarkers dep.w = 1 dep.w = 1 track(A) dep.n = 1 track(C) dep.n = 1 finalizeDepMarkers delete(effect) deps = [DepA, DepC]

九、Mermaid 图表:Vue3 响应式系统全景

Effect执行

触发更新阶段

依赖收集阶段

访问响应式数据

触发Proxy.get

tracktarget, GET, key

targetMap.gettarget

dep.addactiveEffect

访问ref.value

trackRefValue

dep.addactiveEffect

创建阶段

reactiveobj

createReactiveObject

new Proxytarget, handlers

存储到reactiveMap

refvalue

new RefImpl

包装为响应式对象

修改响应式数据

触发Proxy.set

triggertarget, SET, key

targetMap.gettarget

遍历dep执行effect

修改ref.value

triggerRefValue

遍历dep执行effect

effect.scheduler

组件重新渲染

effect.run

直接执行回调


十、代码示例

10.1 手写简化版 Vue3 响应式系统

// 简化版 Vue3 响应式系统

// 全局状态
let activeEffect = null
const targetMap = new WeakMap()

// track:依赖收集
function track(target, key) {
  if (!activeEffect) 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 = new Set()))
  }

  dep.add(activeEffect)
  activeEffect.deps.push(dep)
}

// trigger:触发更新
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  const dep = depsMap.get(key)
  if (dep) {
    const effects = new Set(dep)
    effects.forEach(effect => {
      if (effect !== activeEffect) {
        if (effect.scheduler) {
          effect.scheduler()
        } else {
          effect.run()
        }
      }
    })
  }
}

// reactive
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver)
      track(target, key)
      // 懒代理
      if (typeof res === 'object' && res !== null) {
        return reactive(res)
      }
      return res
    },
    set(target, key, value, receiver) {
      const oldValue = target[key]
      const result = Reflect.set(target, key, value, receiver)
      if (oldValue !== value) {
        trigger(target, key)
      }
      return result
    }
  })
}

// ref
function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      if (newValue !== value) {
        value = newValue
        trigger(refObject, 'value')
      }
    }
  }
  return refObject
}

// effect
function effect(fn, options = {}) {
  const effect = {
    run() {
      activeEffect = effect
      effect.deps = []
      const result = fn()
      activeEffect = null
      return result
    },
    deps: [],
    scheduler: options.scheduler
  }

  if (!options.lazy) {
    effect.run()
  }

  return effect
}

// computed
function computed(getter) {
  let value
  let dirty = true

  const runner = effect(getter, {
    lazy: true,
    scheduler() {
      dirty = true
    }
  })

  return {
    get value() {
      if (dirty) {
        value = runner.run()
        dirty = false
      }
      return value
    }
  }
}

// 使用示例
const state = reactive({ count: 0, name: 'Vue' })
const double = computed(() => state.count * 2)

effect(() => {
  console.log('count:', state.count, 'double:', double.value)
})

state.count++  // 触发更新
state.name = 'Vue3'  // 不影响上面的 effect

10.2 Vue3 响应式 API 对比演示

import { reactive, ref, computed, watch, effectScope } from 'vue'

// reactive:对象响应式
const state = reactive({
  user: { name: '张三', age: 20 },
  items: [1, 2, 3]
})

// 所有操作都能被检测
state.user.name = '李四'     // ✅ 触发更新
state.items.push(4)          // ✅ 触发更新
state.items[0] = 100         // ✅ 触发更新(Vue3 支持!)
delete state.user.age        // ✅ 触发更新(Vue3 支持!)

// ref:基本类型 + 对象
const count = ref(0)
const user = ref({ name: '张三' })

// computed:计算属性(带缓存)
const fullName = computed(() => {
  console.log('computed 执行')
  return state.user.firstName + ' ' + state.user.lastName
})

// watch:侦听器
watch(() => state.user.age, (newVal, oldVal) => {
  console.log('age变化:', oldVal, '->', newVal)
})

// effectScope:批量停止 effect
const scope = effectScope()
scope.run(() => {
  effect(() => console.log(state.count))
  effect(() => console.log(state.name))
})
// 停止 scope 内所有 effect
scope.stop()

十一、常见问题

Q1:Vue3 的 Proxy 能兼容 IE 吗?

不能。Proxy 是 ES6 特性,无法通过 polyfill 完全模拟。Vue3 官方不支持 IE11。如果需要兼容 IE,可以使用 Vue2 或 Vue3 的兼容构建版本(功能受限)。

Q2:reactive 和 ref 在底层有什么关联?

ref 在内部会调用 reactive。当 ref 的值是对象时,会通过 toReactive 将其转换为 reactive

// ref 内部
this._value = isShallow ? value : toReactive(value)

// toReactive
export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value

Q3:为什么 Vue3 的 effect 比 Vue2 的 Watcher 更灵活?

ReactiveEffect 是一个更底层的抽象,它:

  • 不绑定特定的更新逻辑(通过 scheduler 自定义)
  • 支持嵌套和递归
  • 支持 effectScope 批量管理
  • 可以独立使用(不依赖 Vue 组件)

Q4:Vue3 如何处理 Map 和 Set?

Vue3 为集合类型提供了专门的 handlers,拦截 getsetadddeletehasclearforEachkeysvaluesentries 等方法:

const map = reactive(new Map())
map.set('key', 'value')  // ✅ 触发更新
map.get('key')           // ✅ 依赖收集
map.has('key')           // ✅ 依赖收集
map.delete('key')        // ✅ 触发更新

Q5:位标记算法清理依赖的优势是什么?

相比 Vue2 每次创建新的 Set/数组来对比新旧依赖,Vue3 的位标记算法:

  • 时间复杂度更低(O(n) 且常数更小)
  • 内存分配更少(不需要创建临时数据结构)
  • 支持嵌套 effect 的依赖追踪

十二、总结

Vue3 的响应式系统是一次从底层到 API 的全面升级:

对比项 Vue2 Vue3
拦截方式 Object.defineProperty Proxy
核心类 Observer / Dep / Watcher ReactiveEffect + track/trigger
新增属性 不支持(需 Vue.set 原生支持
数组索引 不支持 原生支持
删除属性 不支持(需 Vue.delete 原生支持
Map/Set 不支持 原生支持
嵌套对象 递归初始化 懒代理
依赖清理 创建新数组对比 位标记算法
嵌套 effect 有限支持 完整支持

Vue3 响应式的核心设计思想

  1. 懒代理:访问时才递归代理深层对象,减少初始化开销
  2. 精确追踪:通过 TrackOpTypesTriggerOpTypes 区分不同操作类型
  3. 高效清理:位标记算法实现 O(n) 的依赖清理
  4. 灵活调度:通过 scheduler 支持自定义更新策略
  5. 独立复用@vue/reactivity 可以独立使用,不依赖 Vue 运行时

十三、思考题

  1. 为什么 Vue3 选择使用 Reflect.get/set 而不是直接访问 target[key]
  2. effectscheduler 参数在 Vue 组件渲染中是如何被使用的?
  3. 设计一个场景,演示嵌套 effect 可能导致的循环依赖问题,以及 Vue3 是如何避免的。
  4. 对比 Vue2 的 Dep.target 栈和 Vue3 的 activeEffect 父子链,分析各自的优劣。
  5. 如果让你基于 Vue3 的 @vue/reactivity 实现一个简单的状态管理库,你会如何设计?
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐