Vue 3 核心源码解析 - 第二部分:我的响应式魔法揭秘

嘿,我又回来了!上次我和大家聊了我的架构重生之路,今天我想深入聊聊我最引以为豪的核心能力------响应式系统。如果说架构是我的骨架,那么响应式系统就是我的灵魂。让我带你走进我的"魔法世界",看看我是如何让数据变得"有生命力"的。

🎪 序章:从"笨拙的魔法师"到"响应式大师"

还记得我在 Vue 2 时代的样子吗?那时候的我就像一个刚学会魔法的新手,虽然能让数据"动起来",但手法还很笨拙。我依赖 Object.defineProperty 这个古老的咒语,虽然有效,但限制很多:

  • 🚫 无法监听数组索引变化arr[0] = newValue 我感知不到
  • 🚫 无法监听对象属性的添加删除obj.newProp = value 我也察觉不了
  • 🚫 深度监听性能差:需要递归遍历所有属性,太累了
  • 🚫 API 不够优雅Vue.setVue.delete 这些补丁让我很尴尬

到了 Vue 3,我决定彻底革新我的魔法体系。我拥抱了 Proxy 这个现代化的魔法道具,从此我的响应式能力发生了质的飞跃!

🔮 第一章:Proxy 魔法的觉醒之路

从 Object.defineProperty 到 Proxy 的华丽转身

让我先给你展示一下我的"魔法进化史":

Vue 2 时代的"古典魔法"

javascript 复制代码
// Vue 2 的响应式实现(简化版)
function defineReactive(obj, key, val) {
  const dep = new Dep()
  
  Object.defineProperty(obj, key, {
    get() {
      // 依赖收集
      if (Dep.target) {
        dep.depend()
      }
      return val
    },
    set(newVal) {
      if (newVal === val) return
      val = newVal
      // 触发更新
      dep.notify()
    }
  })
}

// 问题来了:
const obj = { count: 0 }
defineReactive(obj, 'count', 0)

obj.count = 1 // ✅ 能监听到
obj.newProp = 'hello' // ❌ 监听不到,需要 Vue.set
delete obj.count // ❌ 监听不到,需要 Vue.delete

这种方式就像是给每个属性都安装了一个"窃听器",虽然能工作,但有很多盲区。

Vue 3 时代的"现代魔法"

javascript 复制代码
// Vue 3 的响应式实现(简化版)
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      // 依赖收集
      track(target, key)
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      // 触发更新
      trigger(target, key)
      return result
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key)
      // 触发更新
      trigger(target, key)
      return result
    }
  })
}

// 现在所有操作都能监听到了!
const obj = reactive({ count: 0 })
obj.count = 1 // ✅ 能监听到
obj.newProp = 'hello' // ✅ 能监听到
delete obj.count // ✅ 能监听到

Proxy 就像是给整个对象安装了一个"全方位监控系统",任何操作都逃不过我的法眼!

Proxy 的十八般武艺

Proxy 不仅仅是 getset,它还有很多其他的"魔法技能":

javascript 复制代码
const handler = {
  get(target, key, receiver) { /* 属性访问 */ },
  set(target, key, value, receiver) { /* 属性设置 */ },
  has(target, key) { /* in 操作符 */ },
  deleteProperty(target, key) { /* delete 操作符 */ },
  ownKeys(target) { /* Object.keys() */ },
  getOwnPropertyDescriptor(target, key) { /* Object.getOwnPropertyDescriptor() */ },
  defineProperty(target, key, descriptor) { /* Object.defineProperty() */ },
  // ... 还有更多
}

这就是为什么我在 Vue 3 中能够监听到几乎所有的对象操作!

🧙‍♂️ 第二章:响应式系统的核心架构

现在让我带你深入我的响应式系统内部,看看我是如何组织这套"魔法体系"的。

核心概念:响应式数据的三种形态

在我的世界里,响应式数据有三种主要形态:

1. reactive - 深度响应式对象

typescript 复制代码
// packages/reactivity/src/reactive.ts
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> {
  // 如果已经是只读的,直接返回
  if (isReadonly(target)) {
    return target
  }
  
  return createReactiveObject(
    target,
    false, // 不是只读
    mutableHandlers, // 可变对象的处理器
    mutableCollectionHandlers, // 集合类型的处理器
    reactiveMap // 缓存映射
  )
}

reactive 就像是我的"深度魔法",它会递归地让对象的每一层都变成响应式的:

javascript 复制代码
const state = reactive({
  user: {
    name: 'Vue',
    hobbies: ['coding', 'debugging']
  }
})

// 所有层级的变化都能监听到
state.user.name = 'Vue 3' // ✅ 响应式
state.user.hobbies.push('optimizing') // ✅ 响应式
state.user.newProp = 'hello' // ✅ 响应式

2. ref - 基础类型的响应式包装

typescript 复制代码
// packages/reactivity/src/ref.ts
export function ref<T>(value: T): Ref<UnwrapRef<T>> {
  return createRef(value, false)
}

function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue // 已经是 ref 了,直接返回
  }
  return new RefImpl(rawValue, shallow)
}

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) {
    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 : toReactive(newVal)
      triggerRefValue(this) // 触发更新
    }
  }
}

ref 就像是给基础类型穿上了一件"响应式外衣":

javascript 复制代码
const count = ref(0)
const message = ref('Hello')

console.log(count.value) // 0
count.value++ // 触发响应式更新

// ref 也可以包装对象,会自动转换为 reactive
const user = ref({ name: 'Vue' })
user.value.name = 'Vue 3' // 响应式更新

3. computed - 计算属性的魔法

typescript 复制代码
// packages/reactivity/src/computed.ts
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    getter = getterOrOptions
    setter = __DEV__ ? () => { console.warn('Write operation failed: computed value is readonly') } : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
  return cRef as any
}

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: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
    // 创建一个特殊的 effect
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // 通知依赖更新
      }
    })
    this.effect.computed = this
    this.effect.active = this._cacheable = !isSSR
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    const self = toRaw(this)
    trackRefValue(self) // 收集依赖
    
    if (self._dirty || !self._cacheable) {
      self._dirty = false
      self._value = self.effect.run()! // 执行计算
    }
    return self._value
  }

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

computed 是我最聪明的魔法之一,它具有"懒计算"和"缓存"的特性:

javascript 复制代码
const count = ref(0)
const doubled = computed(() => {
  console.log('计算中...') // 只有在需要时才会执行
  return count.value * 2
})

console.log(doubled.value) // 输出: "计算中..." 然后是 0
console.log(doubled.value) // 不会再输出 "计算中...",直接返回缓存值 0

count.value = 1 // 标记 computed 为 dirty
console.log(doubled.value) // 输出: "计算中..." 然后是 2

响应式系统的"大脑" - Effect 系统

现在让我介绍我的响应式系统的"大脑" - Effect 系统。这是整个响应式魔法的核心:

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

  constructor(
    public fn: () => T,
    public scheduler?: EffectScheduler | 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 (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

Effect 系统就像是我的"神经网络",它负责:

  1. 执行副作用函数:当响应式数据变化时,自动执行相关的副作用
  2. 依赖收集:记录哪些响应式数据被访问了
  3. 依赖清理:清理不再需要的依赖关系
  4. 循环依赖检测:防止无限递归调用

🎯 第三章:依赖收集与触发的精妙机制

现在让我揭秘我的响应式系统最核心的部分 - 依赖收集与触发机制。这就像是我的"魔法感应网络"。

依赖收集:我如何知道谁在"偷看"我

typescript 复制代码
// packages/reactivity/src/effect.ts
const targetMap = new WeakMap<any, KeyToDepMap>()

export function track(target: object, type: TrackOpTypes, key: unknown) {
  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()))
    }

    const eventInfo = __DEV__ ? { effect: activeEffect, target, type, key } : undefined
    trackEffects(dep, eventInfo)
  }
}

export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  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) // 反向引用
    
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack({
        effect: activeEffect!,
        ...debuggerEventExtraInfo!
      })
    }
  }
}

这个过程就像是建立一个"监听网络":

javascript 复制代码
// 当这段代码执行时
const state = reactive({ count: 0, name: 'Vue' })

effect(() => {
  console.log(state.count) // 访问 count 属性
  if (state.count > 0) {
    console.log(state.name) // 条件性访问 name 属性
  }
})

// 我的内部数据结构会变成这样:
// targetMap: WeakMap {
//   state对象 => Map {
//     'count' => Set { effect函数 },
//     'name' => Set { effect函数 } // 只有当 count > 0 时才会建立这个依赖
//   }
// }

触发更新:我如何通知所有"关心者"

typescript 复制代码
// packages/reactivity/src/effect.ts
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 // 没有依赖,直接返回
  }

  let deps: (Dep | undefined)[] = []
  
  if (type === TriggerOpTypes.CLEAR) {
    // 清空操作,触发所有依赖
    deps = [...depsMap.values()]
  } else if (key === 'length' && isArray(target)) {
    // 数组长度变化的特殊处理
    const newLength = Number(newValue)
    depsMap.forEach((dep, key) => {
      if (key === 'length' || key >= newLength) {
        deps.push(dep)
      }
    })
  } else {
    // 普通属性变化
    if (key !== void 0) {
      deps.push(depsMap.get(key))
    }

    // 处理添加/删除属性的情况
    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
    }
  }

  const eventInfo = __DEV__ ? { target, type, key, newValue, oldValue, oldTarget } : undefined

  if (deps.length === 1) {
    if (deps[0]) {
      if (__DEV__) {
        triggerEffects(deps[0], eventInfo)
      } else {
        triggerEffects(deps[0])
      }
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    if (__DEV__) {
      triggerEffects(createDep(effects), eventInfo)
    } else {
      triggerEffects(createDep(effects))
    }
  }
}

这个触发机制非常智能,它会根据不同的操作类型来决定触发哪些依赖:

javascript 复制代码
const state = reactive({ 
  list: [1, 2, 3],
  obj: { a: 1 }
})

// 不同操作触发不同的依赖
state.list.push(4) // 触发 list 和 list.length 的依赖
state.obj.b = 2 // 触发 obj 的迭代依赖(因为添加了新属性)
delete state.obj.a // 触发 obj 的迭代依赖(因为删除了属性)

性能优化:位运算的巧妙应用

我在依赖收集中使用了一个非常巧妙的优化技术 - 位运算标记:

typescript 复制代码
// 使用位运算来标记依赖的状态
let trackOpBit = 1
let effectTrackDepth = 0
const maxMarkerBits = 30

// 每个 effect 执行时,都会有一个唯一的位标记
trackOpBit = 1 << ++effectTrackDepth

// 依赖对象上的标记
interface Dep extends Set<ReactiveEffect> {
  w: number // was tracked,之前被追踪的标记
  n: number // newly tracked,新追踪的标记
}

// 检查是否是新依赖
const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0
// 检查是否之前就被追踪了
const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0

这种优化让我在处理复杂的依赖关系时性能大大提升,特别是在处理嵌套的 effect 时。

🚀 第四章:集合类型的特殊魔法

JavaScript 中的 MapSetWeakMapWeakSet 这些集合类型需要特殊的处理,因为它们的操作方式和普通对象不同。

Map 和 Set 的响应式处理

typescript 复制代码
// packages/reactivity/src/collectionHandlers.ts
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
  const instrumentations = shallow
    ? isReadonly
      ? shallowReadonlyInstrumentations
      : shallowInstrumentations
    : isReadonly
    ? readonlyInstrumentations
    : mutableInstrumentations

  return (target: CollectionTypes, key: string | symbol, receiver: CollectionTypes) => {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (key === ReactiveFlags.RAW) {
      return target
    }

    return Reflect.get(
      hasOwn(instrumentations, key) && key in target
        ? instrumentations
        : target,
      key,
      receiver
    )
  }
}

// Map 的特殊处理
const mutableInstrumentations: Record<string, Function> = {
  get(this: MapTypes, key: unknown) {
    return get(this, key)
  },
  get size() {
    return size(this as unknown as IterableCollections)
  },
  has,
  add,
  set,
  delete: deleteEntry,
  clear,
  forEach: createForEach(false, false)
}

function get(target: MapTypes, key: unknown) {
  const rawTarget = toRaw(target)
  const rawKey = toRaw(key)
  
  if (!Object.is(key, rawKey)) {
    track(rawTarget, TrackOpTypes.GET, key)
  }
  track(rawTarget, TrackOpTypes.GET, rawKey)
  
  const { has } = getProto(rawTarget)
  if (has.call(rawTarget, key)) {
    return wrap(rawTarget.get(key))
  } else if (has.call(rawTarget, rawKey)) {
    return wrap(rawTarget.get(rawKey))
  } else if (target !== rawTarget) {
    rawTarget.get(key)
  }
}

function set(this: MapTypes, key: unknown, value: unknown) {
  value = toRaw(value)
  const rawTarget = toRaw(this)
  const { has, get } = getProto(rawTarget)

  let hadKey = has.call(rawTarget, key)
  if (!hadKey) {
    key = toRaw(key)
    hadKey = has.call(rawTarget, key)
  }

  const oldValue = get.call(rawTarget, key)
  rawTarget.set(key, value)
  
  if (!hadKey) {
    trigger(rawTarget, TriggerOpTypes.ADD, key, value)
  } else if (hasChanged(value, oldValue)) {
    trigger(rawTarget, TriggerOpTypes.SET, key, value, oldValue)
  }
  
  return this
}

这样,Map 和 Set 也能享受到完整的响应式能力:

javascript 复制代码
const map = reactive(new Map())
const set = reactive(new Set())

effect(() => {
  console.log('Map size:', map.size)
  console.log('Set size:', set.size)
})

map.set('key', 'value') // 触发 effect
set.add('item') // 触发 effect

🎨 第五章:响应式系统的高级特性

1. shallowReactive - 浅层响应式

有时候我们不需要深度响应式,这时候 shallowReactive 就派上用场了:

typescript 复制代码
// packages/reactivity/src/reactive.ts
export function shallowReactive<T extends object>(target: T): ShallowReactive<T> {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  )
}

// 浅层处理器只在第一层进行响应式处理
const shallowReactiveHandlers: ProxyHandler<object> = {
  get: shallowGet,
  set: shallowSet,
  deleteProperty,
  has,
  ownKeys
}

function shallowGet(target: object, key: string | symbol, receiver: object) {
  const res = Reflect.get(target, key, receiver)
  
  if (!isReadonly) {
    track(target, TrackOpTypes.GET, key)
  }
  
  // 注意:这里不会递归转换为响应式
  return res
}
javascript 复制代码
const state = shallowReactive({
  count: 0,
  nested: { value: 1 }
})

effect(() => {
  console.log(state.count) // 响应式
  console.log(state.nested.value) // 非响应式
})

state.count++ // 触发 effect
state.nested.value++ // 不会触发 effect
state.nested = { value: 2 } // 触发 effect(因为替换了整个对象)

2. readonly - 只读响应式

typescript 复制代码
export function readonly<T extends object>(target: T): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true, // 标记为只读
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  )
}

const readonlyHandlers: ProxyHandler<object> = {
  get: readonlyGet,
  set(target, key) {
    if (__DEV__) {
      warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target)
    }
    return true
  },
  deleteProperty(target, key) {
    if (__DEV__) {
      warn(`Delete operation on key "${String(key)}" failed: target is readonly.`, target)
    }
    return true
  }
}

3. 响应式工具函数

我还提供了一系列实用的工具函数:

typescript 复制代码
// 检查是否为响应式对象
export function isReactive(value: unknown): boolean {
  if (isReadonly(value)) {
    return isReactive((value as Target)[ReactiveFlags.RAW])
  }
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}

// 获取原始对象
export function toRaw<T>(observed: T): T {
  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}

// 标记对象为非响应式
export function markRaw<T extends object>(value: T): T {
  def(value, ReactiveFlags.SKIP, true)
  return value
}

🔧 第六章:性能优化的黑科技

1. 依赖清理的智能算法

我使用了一个非常聪明的算法来清理不再需要的依赖:

typescript 复制代码
function cleanupEffect(effect: ReactiveEffect) {
  const { deps } = effect
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].delete(effect)
    }
    deps.length = 0
  }
}

// 在每次 effect 执行时,我会标记哪些依赖是新的,哪些是旧的
function finalizeDepMarkers(effect: ReactiveEffect) {
  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
  }
}

2. 调度器系统

为了避免不必要的重复执行,我实现了一个智能的调度器系统:

typescript 复制代码
// packages/reactivity/src/effect.ts
const queue: SchedulerJob[] = []
let flushIndex = 0
let isFlushing = false
let isFlushPending = false

export function queueJob(job: SchedulerJob) {
  if (
    !queue.length ||
    !queue.includes(job, isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)
  ) {
    if (job.id == null) {
      queue.push(job)
    } else {
      queue.splice(findInsertionIndex(job.id), 0, job)
    }
    queueFlush()
  }
}

function queueFlush() {
  if (!isFlushing && !isFlushPending) {
    isFlushPending = true
    currentFlushPromise = resolvedPromise.then(flushJobs)
  }
}

这个调度器确保了:

  • 同一个 effect 在一个事件循环中只会执行一次
  • effect 按照优先级顺序执行
  • 避免无限递归调用

3. 内存优化

我使用 WeakMap 来存储响应式对象的元数据,这样当原始对象被垃圾回收时,相关的响应式数据也会被自动清理:

typescript 复制代码
// 使用 WeakMap 避免内存泄漏
export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()

// 依赖映射也使用 WeakMap
const targetMap = new WeakMap<any, KeyToDepMap>()

🎭 第七章:与 Vue 2 的对比 - 我的成长轨迹

让我来对比一下我在 Vue 2 和 Vue 3 中的表现:

功能对比

特性 Vue 2 Vue 3
基础响应式 ✅ Object.defineProperty ✅ Proxy
数组索引监听 ❌ 需要特殊处理 ✅ 原生支持
对象属性添加/删除 ❌ 需要 Vue.set/Vue.delete ✅ 原生支持
Map/Set 支持 ❌ 不支持 ✅ 完整支持
性能 🐌 递归遍历所有属性 🚀 按需代理
类型支持 🤔 Flow,类型推断有限 💪 TypeScript,完美类型推断
Tree-shaking ❌ 不支持 ✅ 完全支持

性能对比

javascript 复制代码
// Vue 2: 初始化时需要递归遍历所有属性
function observe(obj) {
  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key])
    if (typeof obj[key] === 'object') {
      observe(obj[key]) // 递归处理
    }
  })
}

// Vue 3: 按需代理,只有访问时才处理
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      const result = target[key]
      // 只有在访问时才转换为响应式
      return isObject(result) ? reactive(result) : result
    }
  })
}

这种按需处理的方式让我的初始化性能提升了数倍!

🌟 第八章:实战案例 - 手写一个迷你响应式系统

现在让我带你手写一个简化版的响应式系统,帮你更好地理解我的工作原理:

typescript 复制代码
// 迷你版响应式系统
class MiniReactive {
  private targetMap = new WeakMap()
  private activeEffect: Function | null = null

  // 依赖收集
  track(target: object, key: string | symbol) {
    if (!this.activeEffect) return
    
    let depsMap = this.targetMap.get(target)
    if (!depsMap) {
      this.targetMap.set(target, (depsMap = new Map()))
    }
    
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = new Set()))
    }
    
    dep.add(this.activeEffect)
  }

  // 触发更新
  trigger(target: object, key: string | symbol) {
    const depsMap = this.targetMap.get(target)
    if (!depsMap) return
    
    const dep = depsMap.get(key)
    if (dep) {
      dep.forEach((effect: Function) => effect())
    }
  }

  // 创建响应式对象
  reactive<T extends object>(target: T): T {
    return new Proxy(target, {
      get: (target, key, receiver) => {
        this.track(target, key)
        return Reflect.get(target, key, receiver)
      },
      set: (target, key, value, receiver) => {
        const result = Reflect.set(target, key, value, receiver)
        this.trigger(target, key)
        return result
      }
    })
  }

  // 创建副作用函数
  effect(fn: Function) {
    const effectFn = () => {
      this.activeEffect = effectFn
      fn()
      this.activeEffect = null
    }
    effectFn()
  }
}

// 使用示例
const miniReactive = new MiniReactive()

const state = miniReactive.reactive({ count: 0 })

miniReactive.effect(() => {
  console.log('count changed:', state.count)
})

state.count++ // 输出: count changed: 1

虽然这个迷你版本很简单,但它展示了我的核心工作原理!

🎯 尾声:响应式系统的哲学思考

回顾我的响应式系统进化之路,我想分享几个核心的设计哲学:

1. 透明性原则

"最好的魔法是让人感觉不到魔法的存在。"

我的响应式系统设计得尽可能透明,开发者可以像操作普通对象一样操作响应式对象,而不需要学习特殊的 API。

2. 性能优先

"性能不是事后优化,而是设计时的考量。"

从 Proxy 的选择到位运算的优化,从按需代理到智能调度,每一个设计决策都考虑了性能因素。

3. 类型安全

"类型系统不是束缚,而是自由的保障。"

通过 TypeScript 的类型系统,我能够在编译时就发现潜在的问题,让开发者写出更安全的代码。

4. 可扩展性

"好的架构应该能够适应未来的变化。"

我的响应式系统设计得足够灵活,可以轻松扩展到新的数据类型和使用场景。

📚 下期预告

在下一篇文章中,我将深入讲解组合式 API 的实现原理。我会告诉你:

  • setup() 函数是如何工作的
  • 生命周期钩子的实现机制
  • 依赖注入系统的设计
  • 如何与我的响应式系统完美结合

敬请期待!


💡 小贴士:想要深入理解我的响应式系统,建议按照这个顺序阅读源码:

  1. packages/reactivity/src/reactive.ts - 了解响应式对象的创建
  2. packages/reactivity/src/ref.ts - 理解 ref 的实现
  3. packages/reactivity/src/effect.ts - 掌握依赖收集和触发机制
  4. packages/reactivity/src/computed.ts - 学习计算属性的实现
    🎯 实践建议:试着实现一个自己的迷你响应式系统,这会让你对我的工作原理有更深的理解。

相关推荐
CodeTransfer几秒前
今天给大家搬运的是四角线框hover效果
前端·vue.js
归于尽2 分钟前
别让类名打架!CSS 模块化教你给样式上 "保险"
前端·css·react.js
凤凰AI29 分钟前
Python知识点4-嵌套循环&break和continue使用&死循环
开发语言·前端·python
Lazy_zheng42 分钟前
虚拟 DOM 到底是啥?为什么 React 要用它?
前端·javascript·react.js
多啦C梦a43 分钟前
前端按钮大撞衫,CSS 模块化闪亮登场!
前端·javascript·面试
拾光拾趣录1 小时前
WebRTC深度解析:从原理到实战
前端·webrtc
TreeNewBeeMVP1 小时前
Vue 3 核心原理剖析:响应式、编译与运行时优化
前端
哒哒哒5285201 小时前
vue3基础知识
前端
FogLetter1 小时前
受控组件 vs 非受控组件:React表单的双面哲学
前端·react.js
拾光拾趣录1 小时前
前端工程化 | package.json 中的 sideEffects 属性
前端·前端工程化