深入理解 Vue 3 响应式系统

从 Proxy 拦截到 effect 调度,一步步拆解 Vue 3 reactivity 包的核心实现。

2 分钟阅读327 字

一、概述

Vue 3 的响应式系统由 @vue/reactivity 包承载,仅约 1000 行 TypeScript,却支撑起了整个框架的运行时。本文带你从 reactive 入手,理解依赖收集、派发更新与调度器之间的协作。

二、背景

在 Vue 2 中,响应式基于 Object.defineProperty 实现,存在无法监听新增属性、数组下标等缺陷。Vue 3 改用 ES2015 Proxy,从语言层面解决了上述痛点,并天然支持 Map / Set / WeakMap / WeakSet。

三、核心实现

3.1 reactive:创建代理

TS
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> {
  if (isReadonly(target)) return target as any
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

createReactiveObject 会缓存已代理的对象,避免重复包装。

3.2 track & trigger

TS
export 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)
}

export function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  effects && effects.forEach((effect) => queueJob(effect.scheduler ? effect.scheduler : effect))
}

3.3 effect 调度

调度器 queueJob 会把 effect 推入微任务队列,同一 tick 内多次更新只会触发一次回调,这是 Vue 3 性能优于 Vue 2 的关键之一。

TS
let queue: Function[] = []
const resolvedPromise = Promise.resolve()
let flushPromise: Promise<void> | null = null

export function queueJob(job: Function) {
  if (!queue.includes(job)) queue.push(job)
  if (!flushPromise) {
    flushPromise = resolvedPromise.then(flushJobs)
  }
}

四、实践技巧

  1. 避免深响应:扁平数据结构用 shallowRef / shallowReactive,配合 triggerRef 主动通知。
  2. 解构会丢失响应性:使用 toRefs(state) 保持解构后的属性仍是 ref。
  3. 计算属性 vs effect:能用 computed 就不要写 effect,前者自带缓存。

五、总结

Vue 3 的响应式系统是一套基于 Proxy + 依赖收集 + 任务调度的最小完备实现。理解它能帮助我们写出更高效的组件,并避免常见陷阱。

推荐阅读:Vue.js 源码 - reactivity


文章 slug: vue3-reactivity-deep-dive 最后更新: 2026-06-12

版权声明 · CC BY-NC-ND 4.0

署名-非商业性使用-禁止演绎 4.0 国际

版权归属

本作品著作权归 窦长友 所有, 首次发布于 ,受相关知识产权法律法规保护。

授权范围
  • 可自由分享 — 在任何媒介以任何形式复制、转载本文
  • 不得用于商业目的 — 未经书面授权禁止商用
  • 禁止演绎修改 — 不得改编、转换或以本文为基础再创作
署名要求

转载或引用时须明确标注作者姓名原文出处及本许可协议链接。 不得以任何方式暗示或声称作者为您的使用背书。

评论

由 GitHub Discussions 驱动