📅 发布日期:2025年10月24日
🏷️ 标签:Vue3、响应式、Proxy、Reflect、原理
⏱️ 阅读时长:约125分钟
💡 难度:⭐⭐⭐ 高级

📖 前言

大家好,我是【代码小库】的做者。这是 Vue3 实战系列 的第3篇文章。

在前两篇中,我们学习了项目搭建和 Composition API。今天,我们将深入 Vue3 的响应式系统原理,理解数据驱动视图更新的底层机制。

本文你将学到:

  • ✅ Vue2 和 Vue3 响应式系统的区别
  • ✅ Proxy 和 Reflect 的核心 API 和使用
  • ✅ Vue3 响应式系统的实现原理
  • ✅ 依赖收集(track)和派发更新(trigger)机制
  • ✅ 手写一个简易的响应式系统
  • ✅ 常见问题和性能优化

🔄 一、Vue2 vs Vue3 响应式系统对比

1.1 Vue2 的响应式实现(Object.defineProperty)

Vue2 使用 Object.defineProperty 实现响应式:

// Vue2 的实现方式
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get() {
      console.log(`读取 ${key}: ${val}`)
      // 收集依赖
      return val
    },
    set(newVal) {
      if (newVal !== val) {
        console.log(`设置 ${key}: ${newVal}`)
        val = newVal
        // 触发更新
      }
    }
  })
}

// 使用
const obj = {}
defineReactive(obj, 'name', 'John')

console.log(obj.name)  // 读取 name: John
obj.name = 'Tom'       // 设置 name: Tom

Vue2 的局限性:

const obj = { name: 'John' }

// ❌ 问题1:无法检测新增属性
obj.age = 25  // 不会触发视图更新
// 解决:需要使用 Vue.set(obj, 'age', 25)

// ❌ 问题2:无法检测删除属性
delete obj.name  // 不会触发视图更新
// 解决:需要使用 Vue.delete(obj, 'name')

// ❌ 问题3:数组索引修改无法检测
const arr = [1, 2, 3]
arr[0] = 100  // 不会触发视图更新
// 解决:需要使用 Vue.set(arr, 0, 100) 或 arr.splice(0, 1, 100)

// ❌ 问题4:数组长度修改无法检测
arr.length = 0  // 不会触发视图更新

1.2 Vue3 的响应式实现(Proxy)

Vue3 使用 Proxy 实现响应式,完美解决了 Vue2 的所有问题:

const obj = new Proxy(
  { name: 'John' },
  {
    get(target, key) {
      console.log(`读取 ${key}: ${target[key]}`)
      return target[key]
    },
    set(target, key, newValue) {
      console.log(`设置 ${key}: ${newValue}`)
      target[key] = newValue
      return true
    },
    deleteProperty(target, key) {
      console.log(`删除 ${key}`)
      delete target[key]
      return true
    }
  }
)

// ✅ 新增属性 - 可以检测
obj.age = 25  // 设置 age: 25

// ✅ 删除属性 - 可以检测
delete obj.name  // 删除 name

// ✅ 数组操作 - 全都可以检测
const arr = new Proxy([1, 2, 3], {
  set(target, key, newValue) {
    console.log(`设置 arr[${key}] = ${newValue}`)
    target[key] = newValue
    return true
  }
})

arr[0] = 100      // 设置 arr[0] = 100
arr.length = 0    // 设置 arr[length] = 0

Vue3 的优势:

特性 Vue2 (Object.defineProperty) Vue3 (Proxy)
新增属性 ❌ 需要Vue.set ✅ 自动检测
删除属性 ❌ 需要Vue.delete ✅ 自动检测
数组索引修改 ❌ 需要特殊处理 ✅ 自动检测
数组长度修改 ❌ 需要特殊处理 ✅ 自动检测
性能 ⭐⭐⭐ 需要遍历所有属性 ⭐⭐⭐⭐⭐ 懒代理
浏览器兼容性 ✅ IE9+ ⚠️ 不支持 IE11

🎯 二、Proxy 和 Reflect 基础

2.1 Proxy 完整 API

const proxy = new Proxy(target, handler)

handler 支持的拦截操作:

const handler = {
  // 1. 读取属性
  get(target, key, receiver) {
    console.log(`读取 ${key}`)
    return Reflect.get(target, key, receiver)
  },
  
  // 2. 设置属性
  set(target, key, value, receiver) {
    console.log(`设置 ${key} = ${value}`)
    return Reflect.set(target, key, value, receiver)
  },
  
  // 3. 检查属性是否存在 (in 操作符)
  has(target, key) {
    console.log(`检查 ${key} 是否存在`)
    return Reflect.has(target, key)
  },
  
  // 4. 删除属性 (delete 操作符)
  deleteProperty(target, key) {
    console.log(`删除 ${key}`)
    return Reflect.deleteProperty(target, key)
  },
  
  // 5. 获取自身属性键 (Object.keys)
  ownKeys(target) {
    console.log('获取所有键')
    return Reflect.ownKeys(target)
  },
  
  // 6. 获取属性描述符
  getOwnPropertyDescriptor(target, key) {
    return Reflect.getOwnPropertyDescriptor(target, key)
  },
  
  // 7. 定义属性
  defineProperty(target, key, descriptor) {
    return Reflect.defineProperty(target, key, descriptor)
  },
  
  // 8. 获取原型
  getPrototypeOf(target) {
    return Reflect.getPrototypeOf(target)
  },
  
  // 9. 设置原型
  setPrototypeOf(target, proto) {
    return Reflect.setPrototypeOf(target, proto)
  },
  
  // 10. 检查对象是否可扩展
  isExtensible(target) {
    return Reflect.isExtensible(target)
  },
  
  // 11. 阻止对象扩展
  preventExtensions(target) {
    return Reflect.preventExtensions(target)
  },
  
  // 12. 函数调用拦截 (apply)
  apply(target, thisArg, argsList) {
    console.log('函数被调用')
    return Reflect.apply(target, thisArg, argsList)
  },
  
  // 13. new 操作符拦截
  construct(target, argsList, newTarget) {
    console.log('构造函数被调用')
    return Reflect.construct(target, argsList, newTarget)
  }
}

2.2 Reflect 的作用

为什么使用 Reflect?

const obj = { name: 'John' }

// ❌ 直接操作 - 可能出错
try {
  Object.defineProperty(obj, 'age', { value: 25 })
} catch (error) {
  console.error('操作失败')
}

// ✅ 使用 Reflect - 返回布尔值表示成功/失败
const success = Reflect.defineProperty(obj, 'age', { value: 25 })
if (!success) {
  console.error('操作失败')
}

Reflect 的三大优势:

  1. 函数式编程风格 - 所有操作都返回有意义的值
  2. 与 Proxy 一一对应 - 更统一的 API
  3. 更合理的返回值 - 布尔值表示操作成功/失败

实例:Proxy + Reflect 结合使用

const person = {
  name: 'John',
  age: 25
}

const proxy = new Proxy(person, {
  get(target, key, receiver) {
    console.log(`读取 ${key}`)
    // receiver 指向代理对象,确保 this 指向正确
    return Reflect.get(target, key, receiver)
  },
  
  set(target, key, value, receiver) {
    console.log(`设置 ${key} = ${value}`)
  
    // 验证:age 必须是数字
    if (key === 'age' && typeof value !== 'number') {
      throw new TypeError('age 必须是数字')
    }
  
    return Reflect.set(target, key, value, receiver)
  }
})

// 使用
proxy.name = 'Tom'     // 设置 name = Tom
proxy.age = 30         // 设置 age = 30
proxy.age = '30'       // 抛出错误: age 必须是数字

🔧 三、Vue3 响应式系统核心实现

3.1 核心数据结构

// 依赖收集的核心数据结构
type Dep = Set<ReactiveEffect>  // 依赖集合
type KeyToDepMap = Map<any, Dep>  // 键 -> 依赖集合
const targetMap = new WeakMap<any, KeyToDepMap>()  // 对象 -> 键映射

// 响应式副作用函数
interface ReactiveEffect {
  (): void
  deps: Dep[]  // 该 effect 依赖的所有 dep
}

// 当前正在执行的 effect
let activeEffect: ReactiveEffect | undefined

数据结构关系:

targetMap (WeakMap)
  ├─ target1 (原始对象) → depsMap (Map)
  │    ├─ key1 → dep (Set) → [effect1, effect2, ...]
  │    ├─ key2 → dep (Set) → [effect3, ...]
  │    └─ key3 → dep (Set) → [effect1, effect4, ...]
  │
  └─ target2 (原始对象) → depsMap (Map)
       ├─ key1 → dep (Set) → [effect5, ...]
       └─ key2 → dep (Set) → [effect6, effect7, ...]

3.2 reactive - 创建响应式对象

/**
 * 创建响应式对象
 */
function reactive<T extends object>(target: T): T {
  // 如果已经是代理对象,直接返回
  if (isProxy(target)) {
    return target
  }
  
  return new Proxy(target, {
    get(target, key, receiver) {
      // 特殊标记:判断是否是响应式对象
      if (key === '__v_isReactive') {
        return true
      }
    
      const result = Reflect.get(target, key, receiver)
    
      // 依赖收集
      track(target, key)
    
      // 如果属性值是对象,递归创建响应式对象(懒代理)
      if (isObject(result)) {
        return reactive(result)
      }
    
      return result
    },
  
    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
    },
  
    deleteProperty(target, key) {
      // 检查属性是否存在
      const hadKey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
    
      // 如果成功删除了存在的属性,触发更新
      if (result && hadKey) {
        trigger(target, key)
      }
    
      return result
    }
  })
}

// 工具函数
function isObject(val: any): boolean {
  return val !== null && typeof val === 'object'
}

function hasOwn(target: object, key: string | symbol): boolean {
  return Object.prototype.hasOwnProperty.call(target, key)
}

function isProxy(value: any): boolean {
  return !!(value && value.__v_isReactive)
}

3.3 ref - 创建响应式引用

/**
 * ref 的实现
 */
class RefImpl<T> {
  private _value: T
  public dep: Dep = new Set()
  public readonly __v_isRef = true
  
  constructor(private _rawValue: T) {
    // 如果是对象,转换为 reactive
    this._value = convert(_rawValue)
  }
  
  get value() {
    // 依赖收集
    trackRefValue(this)
    return this._value
  }
  
  set value(newValue: T) {
    // 只有值确实改变了才更新
    if (hasChanged(newValue, this._rawValue)) {
      this._rawValue = newValue
      this._value = convert(newValue)
      // 触发更新
      triggerRefValue(this)
    }
  }
}

function ref<T>(value: T) {
  return new RefImpl(value)
}

// 转换函数:对象转 reactive,基本类型直接返回
function convert<T>(value: T): T {
  return isObject(value) ? reactive(value) : value
}

// 判断值是否改变
function hasChanged(value: any, oldValue: any): boolean {
  return !Object.is(value, oldValue)
}

3.4 track - 依赖收集

/**
 * 依赖收集
 * @param target 原始对象
 * @param key 属性键
 */
function track(target: object, key: unknown) {
  // 如果没有 activeEffect,说明不是在 effect 中访问的,不需要收集
  if (!activeEffect) {
    return
  }
  
  // 1. 从 targetMap 中获取该对象的 depsMap
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  
  // 2. 从 depsMap 中获取该属性的 dep
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  
  // 3. 将当前 effect 添加到 dep 中
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    // 双向记录:effect 也记录它依赖的 dep
    activeEffect.deps.push(dep)
  }
}

/**
 * ref 的依赖收集
 */
function trackRefValue(ref: RefImpl<any>) {
  if (activeEffect) {
    trackEffects(ref.dep)
  }
}

function trackEffects(dep: Dep) {
  if (!dep.has(activeEffect!)) {
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
  }
}

3.5 trigger - 派发更新

/**
 * 派发更新
 * @param target 原始对象
 * @param key 属性键
 */
function trigger(target: object, key: unknown) {
  // 1. 获取该对象的 depsMap
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    return
  }
  
  // 2. 获取该属性的 dep
  const dep = depsMap.get(key)
  if (!dep) {
    return
  }
  
  // 3. 执行所有 effect
  triggerEffects(dep)
}

/**
 * 执行所有 effect
 */
function triggerEffects(dep: Dep) {
  // 创建副本,避免在遍历时修改 Set
  const effects = [...dep]
  
  effects.forEach((effect) => {
    // 如果 effect 有 scheduler,执行 scheduler 而不是 effect 本身
    if (effect.scheduler) {
      effect.scheduler()
    } else {
      effect()
    }
  })
}

/**
 * ref 的派发更新
 */
function triggerRefValue(ref: RefImpl<any>) {
  triggerEffects(ref.dep)
}

3.6 effect - 副作用函数

/**
 * 创建副作用函数
 */
function effect<T = any>(
  fn: () => T,
  options?: {
    lazy?: boolean
    scheduler?: (effect: ReactiveEffect) => void
  }
) {
  const _effect = function reactiveEffect() {
    // 清除之前的依赖
    cleanup(_effect)
  
    // 设置为当前活跃的 effect
    activeEffect = _effect
  
    // 执行原始函数
    const result = fn()
  
    // 重置
    activeEffect = undefined
  
    return result
  } as ReactiveEffect
  
  _effect.deps = []
  _effect.scheduler = options?.scheduler
  
  // 如果不是 lazy,立即执行
  if (!options?.lazy) {
    _effect()
  }
  
  return _effect
}

/**
 * 清除 effect 的所有依赖
 */
function cleanup(effect: ReactiveEffect) {
  const { deps } = effect
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].delete(effect)
    }
    deps.length = 0
  }
}

🎬 四、响应式系统工作流程

4.1 完整流程示例

import { reactive, effect } from './reactivity'

// 1. 创建响应式对象
const state = reactive({
  count: 0,
  message: 'Hello'
})

// 2. 创建副作用函数(会自动执行一次)
effect(() => {
  console.log(`count: ${state.count}`)
  console.log(`message: ${state.message}`)
})
// 输出:
// count: 0
// message: Hello

// 3. 修改 count(触发更新)
state.count = 1
// 输出:
// count: 1
// message: Hello

// 4. 修改 message(触发更新)
state.message = 'Hi'
// 输出:
// count: 1
// message: Hi

执行流程分析:

1. 执行 reactive(obj)
   └─> 创建 Proxy 对象

2. 执行 effect(() => { console.log(state.count) })
   ├─> 执行 effect 函数
   ├─> 设置 activeEffect = effect
   ├─> 执行回调函数
   │   └─> 访问 state.count
   │       └─> 触发 get 拦截
   │           └─> 执行 track(state, 'count')
   │               └─> 收集依赖: targetMap.set(state, { count: Set([effect]) })
   └─> 重置 activeEffect = undefined

3. 执行 state.count = 1
   ├─> 触发 set 拦截
   └─> 执行 trigger(state, 'count')
       └─> 从 targetMap 中找到 effect
           └─> 执行 effect() (重新运行回调函数)
               └─> 输出: count: 1

4.2 嵌套 effect 的处理

const state = reactive({ count: 0, double: 0 })

// 外层 effect
effect(() => {
  console.log('外层 effect')
  
  // 内层 effect
  effect(() => {
    console.log('内层 effect')
    state.double = state.count * 2
  })
  
  console.log(`count: ${state.count}`)
})

// 输出:
// 外层 effect
// 内层 effect
// count: 0

state.count = 1
// 输出:
// 内层 effect  (内层 effect 重新执行)
// 外层 effect  (外层 effect 重新执行)
// 内层 effect  (外层 effect 中的内层 effect 重新执行)
// count: 1

解决嵌套问题:使用 effect 栈

const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined

function effect(fn: Function) {
  const _effect = function reactiveEffect() {
    cleanup(_effect)
  
    // 压入栈
    effectStack.push(_effect)
    activeEffect = _effect
  
    const result = fn()
  
    // 弹出栈
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  
    return result
  } as ReactiveEffect
  
  _effect.deps = []
  _effect()
  
  return _effect
}

🎨 五、computed - 计算属性的实现

class ComputedRefImpl<T> {
  private _value: T
  private _dirty = true  // 是否需要重新计算
  public readonly effect: ReactiveEffect
  public readonly dep: Dep = new Set()
  public readonly __v_isRef = true
  
  constructor(getter: () => T) {
    // 创建 effect,但不立即执行(lazy: true)
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        // 当依赖变化时,标记为 dirty
        if (!this._dirty) {
          this._dirty = true
          // 触发依赖这个 computed 的 effect
          triggerRefValue(this)
        }
      }
    })
  }
  
  get value() {
    // 依赖收集
    trackRefValue(this)
  
    // 只有 dirty 时才重新计算
    if (this._dirty) {
      this._dirty = false
      this._value = this.effect()
    }
  
    return this._value
  }
}

function computed<T>(getter: () => T) {
  return new ComputedRefImpl(getter)
}

使用示例:

const state = reactive({ count: 0 })

const double = computed(() => {
  console.log('计算 double')
  return state.count * 2
})

console.log(double.value)  // 计算 double, 输出: 0
console.log(double.value)  // 不重新计算,直接返回缓存值 0

state.count = 1
console.log(double.value)  // 计算 double, 输出: 2
console.log(double.value)  // 不重新计算,直接返回缓存值 2

computed 的优势:

  • 懒计算 - 只有访问时才计算
  • 缓存 - 依赖不变时不重新计算
  • 自动依赖追踪 - 自动收集依赖

🎯 六、watch - 侦听器的实现

function watch<T>(
  source: () => T | T,
  callback: (newValue: T, oldValue: T) => void,
  options?: {
    immediate?: boolean
    deep?: boolean
  }
) {
  // 统一处理 source
  let getter: () => T
  
  if (typeof source === 'function') {
    getter = source as () => T
  } else {
    // 如果是 ref,转换为 getter
    getter = () => (source as any).value
  }
  
  let oldValue: T
  
  // 深度遍历(用于 deep 选项)
  const traverse = (value: any, seen = new Set()) => {
    if (!isObject(value) || seen.has(value)) {
      return value
    }
    seen.add(value)
  
    if (Array.isArray(value)) {
      for (let i = 0; i < value.length; i++) {
        traverse(value[i], seen)
      }
    } else {
      for (const key in value) {
        traverse(value[key], seen)
      }
    }
  
    return value
  }
  
  // 包装 getter(处理 deep 选项)
  const wrappedGetter = options?.deep
    ? () => traverse(getter())
    : getter
  
  // 创建 effect
  const job = () => {
    const newValue = wrappedGetter()
    callback(newValue, oldValue)
    oldValue = newValue
  }
  
  const effectFn = effect(wrappedGetter, {
    lazy: true,
    scheduler: job
  })
  
  // immediate 选项:立即执行
  if (options?.immediate) {
    job()
  } else {
    oldValue = effectFn()
  }
}

使用示例:

const state = reactive({ count: 0 })

// 基本使用
watch(
  () => state.count,
  (newValue, oldValue) => {
    console.log(`count 从 ${oldValue} 变成了 ${newValue}`)
  }
)

state.count = 1  // 输出: count 从 0 变成了 1

// immediate 选项
watch(
  () => state.count,
  (newValue, oldValue) => {
    console.log(`当前 count: ${newValue}`)
  },
  { immediate: true }
)
// 立即输出: 当前 count: 1

// deep 选项
const state2 = reactive({
  user: {
    name: 'John',
    age: 25
  }
})

watch(
  () => state2.user,
  (newValue) => {
    console.log('user 变化了', newValue)
  },
  { deep: true }
)

state2.user.age = 26  // 触发侦听器

💡 七、完整的响应式系统实现

现在,让我们整合所有代码,实现一个完整的迷你响应式系统:

// ============ 类型定义 ============
type Dep = Set<ReactiveEffect>
type KeyToDepMap = Map<any, Dep>

interface ReactiveEffect {
  (): any
  deps: Dep[]
  scheduler?: (effect: ReactiveEffect) => void
}

// ============ 全局变量 ============
const targetMap = new WeakMap<any, KeyToDepMap>()
const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined

// ============ 工具函数 ============
function isObject(val: any): boolean {
  return val !== null && typeof val === 'object'
}

function hasChanged(value: any, oldValue: any): boolean {
  return !Object.is(value, oldValue)
}

// ============ reactive 实现 ============
function reactive<T extends object>(target: T): T {
  return new Proxy(target, {
    get(target, key, receiver) {
      if (key === '__v_isReactive') {
        return true
      }
    
      const result = Reflect.get(target, key, receiver)
      track(target, key)
    
      if (isObject(result)) {
        return reactive(result)
      }
    
      return result
    },
  
    set(target, key, value, receiver) {
      const oldValue = target[key]
      const result = Reflect.set(target, key, value, receiver)
    
      if (hasChanged(value, oldValue)) {
        trigger(target, key)
      }
    
      return result
    },
  
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key)
      trigger(target, key)
      return result
    }
  })
}

// ============ ref 实现 ============
class RefImpl<T> {
  private _value: T
  public dep: Dep = new Set()
  public readonly __v_isRef = true
  
  constructor(private _rawValue: T) {
    this._value = isObject(_rawValue) ? reactive(_rawValue) : _rawValue
  }
  
  get value() {
    trackEffects(this.dep)
    return this._value
  }
  
  set value(newValue: T) {
    if (hasChanged(newValue, this._rawValue)) {
      this._rawValue = newValue
      this._value = isObject(newValue) ? reactive(newValue) : newValue
      triggerEffects(this.dep)
    }
  }
}

function ref<T>(value: T) {
  return new RefImpl(value)
}

// ============ track / trigger ============
function track(target: object, key: unknown) {
  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()))
  }
  
  trackEffects(dep)
}

function trackEffects(dep: Dep) {
  if (activeEffect && !dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}

function trigger(target: object, key: unknown) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  
  const dep = depsMap.get(key)
  if (dep) {
    triggerEffects(dep)
  }
}

function triggerEffects(dep: Dep) {
  const effects = [...dep]
  effects.forEach((effect) => {
    if (effect.scheduler) {
      effect.scheduler(effect)
    } else {
      effect()
    }
  })
}

// ============ effect 实现 ============
function cleanup(effect: ReactiveEffect) {
  const { deps } = effect
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].delete(effect)
    }
    deps.length = 0
  }
}

function effect<T = any>(
  fn: () => T,
  options?: {
    lazy?: boolean
    scheduler?: (effect: ReactiveEffect) => void
  }
) {
  const _effect = function reactiveEffect() {
    cleanup(_effect)
  
    effectStack.push(_effect)
    activeEffect = _effect
  
    const result = fn()
  
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  
    return result
  } as ReactiveEffect
  
  _effect.deps = []
  _effect.scheduler = options?.scheduler
  
  if (!options?.lazy) {
    _effect()
  }
  
  return _effect
}

// ============ computed 实现 ============
class ComputedRefImpl<T> {
  private _value: T
  private _dirty = true
  public readonly effect: ReactiveEffect
  public readonly dep: Dep = new Set()
  public readonly __v_isRef = true
  
  constructor(getter: () => T) {
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        if (!this._dirty) {
          this._dirty = true
          triggerEffects(this.dep)
        }
      }
    })
  }
  
  get value() {
    trackEffects(this.dep)
  
    if (this._dirty) {
      this._dirty = false
      this._value = this.effect()
    }
  
    return this._value
  }
}

function computed<T>(getter: () => T) {
  return new ComputedRefImpl(getter)
}

// ============ 导出 ============
export { reactive, ref, effect, computed, track, trigger }

完整示例:

// 使用我们实现的响应式系统
const state = reactive({
  count: 0,
  user: {
    name: 'John',
    age: 25
  }
})

// 计算属性
const double = computed(() => state.count * 2)

// 副作用函数
effect(() => {
  console.log(`count: ${state.count}, double: ${double.value}`)
  console.log(`user.name: ${state.user.name}`)
})
// 输出:
// count: 0, double: 0
// user.name: John

// 修改数据
state.count = 1
// 输出:
// count: 1, double: 2
// user.name: John

state.user.name = 'Tom'
// 输出:
// count: 1, double: 2
// user.name: Tom

// 新增属性(Proxy 可以检测到)
state.user.email = 'tom@example.com'
// 输出:
// count: 1, double: 2
// user.name: Tom

⚡ 八、性能优化

8.1 避免不必要的依赖收集

const state = reactive({ count: 0, message: 'Hello' })

// ❌ 不好:即使不访问 message,也会收集依赖
effect(() => {
  if (state.count > 0) {
    console.log(state.message)
  }
})

// ✅ 好:只在需要时收集依赖
effect(() => {
  const count = state.count
  if (count > 0) {
    console.log(state.message)
  }
})

8.2 使用 shallowReactive 减少深度代理

// 深度响应式(默认)
const deep = reactive({
  level1: {
    level2: {
      level3: { value: 1 }
    }
  }
})

// 浅响应式(只代理第一层)
import { shallowReactive } from 'vue'

const shallow = shallowReactive({
  level1: {
    level2: {
      level3: { value: 1 }
    }
  }
})

// shallow.level1 是响应式的
shallow.level1 = { level2: { level3: { value: 2 } } }  // 触发更新

// shallow.level1.level2 不是响应式的
shallow.level1.level2.level3.value = 2  // 不触发更新

8.3 使用 readonly 避免意外修改

import { reactive, readonly } from 'vue'

const state = reactive({ count: 0 })
const readonlyState = readonly(state)

readonlyState.count = 1  // 警告:无法修改 readonly 对象

🎉 九、总结

本文深入讲解了 Vue3 响应式系统的实现原理:

  1. Proxy vs Object.defineProperty - Vue3 使用 Proxy 解决了 Vue2 的所有局限
  2. 核心实现 - reactive、ref、effect、computed 的完整实现
  3. 依赖收集和派发更新 - track 和 trigger 的机制
  4. 数据结构 - targetMap、depsMap、dep 的关系
  5. 性能优化 - shallowReactive、readonly 等优化手段

下一篇预告:

在下一篇文章中,我将讲解 Pinia 状态管理最佳实践,包括:

  • Pinia 的核心概念和使用
  • Store 的定义和组织
  • 状态、Getters、Actions 的最佳实践
  • Pinia vs Vuex 的对比
  • 实战案例:用户认证、购物车

📦 获取完整源码

本文的完整响应式系统源码(含 TypeScript 类型、注释、测试用例),已经整理好了。

🔍 百度/Google 搜索:代码小库

在网站首页搜索「Vue3 响应式原理」即可找到:

  • ✅ 完整的响应式系统实现(500+ 行代码)
  • ✅ 包含 reactive、ref、computed、watch 等所有 API
  • ✅ 完整的单元测试
  • ✅ 视频讲解(手写响应式系统)

📢 关于作者

前端开发者,【代码小库】创建者,专注 Vue/React/Nuxt 实战教程。

🎯 我的公众号:代码小库

关注后回复关键词获取资源:

  • 回复 vue3 → Vue3 实战源码包
  • 回复 响应式 → 响应式系统完整源码
  • 回复 进群 → 加入技术交流群

💡 本文是【Vue3 实战系列】第 3 篇,完整系列 30 篇,持续更新中…

📚 往期回顾:

  • ✅ 第1篇:Vue3 + Vite 项目搭建完整指南
  • ✅ 第2篇:Vue3 组合式API实战:从Options到Composition

🔜 下期预告:

  • ⏳ 第4篇:Pinia 状态管理最佳实践【替代Vuex】

❤️ 觉得有帮助? 点赞👍 收藏⭐ 关注👀
💬 有疑问? 欢迎评论区留言,我会认真回复每一条!


#Vue3 #响应式原理 #Proxy #Reflect #源码解析 #代码小库

Logo

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

更多推荐