Skip to content

Vue 3 响应式原理 (Reactivity)

Vue 3 使用 Proxy 重构了响应式系统,解决了 Vue 2 中数组监听、属性添加等限制。

1. 核心概念与 Flags

constants.ts 定义了响应式系统中使用的一些核心枚举标志。

typescript
export enum ReactiveFlags {
  SKIP = '__v_skip',             // 跳过不进行响应式处理
  IS_REACTIVE = '__v_isReactive',// 是否是 Reactive 对象
  IS_READONLY = '__v_isReadonly',// 是否是 Readonly 对象
  IS_SHALLOW = '__v_isShallow',  // 是否是浅层响应
  RAW = '__v_raw',               // 获取原始对象
  IS_REF = '__v_isRef',          // 是否是 Ref
}

export enum TrackOpTypes {
  GET = 'get',
  HAS = 'has',
  ITERATE = 'iterate',
}

2. Ref 实现原理

Ref 用于包装基本类型(也可以是对象),通过 .value 访问。

typescript
class RefImpl<T = any> {
  private _rawValue: T
  private _value: T
  dep: Dep = new Dep() // 依赖收集容器

  public readonly [ReactiveFlags.IS_REF] = true

  constructor(value: T, isShallow: boolean) {
    // 如果不是浅层响应,且是对象,则转换为 reactive 对象
    this._rawValue = isShallow ? value : toRaw(value)
    this._value = isShallow ? value : toReactive(value)
  }

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

  set value(newValue) {
    // 检查值是否变化
    if (hasChanged(newValue, this._rawValue)) {
      this._rawValue = newValue
      this._value = toReactive(newValue)
      this.dep.trigger() // 触发更新
    }
  }
}

export function ref(value?: unknown) {
  return new RefImpl(value, false)
}

3. Reactive 实现原理

reactive 只能用于对象,核心是 Proxy

typescript
/* reactive.ts (Simplified) */
export function reactive(target: object) {
  if (isReadonly(target)) return target;
  
  // 创建响应式对象
  return createReactiveObject(target, false, mutableHandlers, reactiveMap);
}

function createReactiveObject(target: object, isReadonly: boolean, baseHandlers: ProxyHandler<any>, proxyMap: WeakMap<any, any>) {
  if (!isObject(target)) return target;
  
  // 检查缓存
  const existingProxy = proxyMap.get(target);
  if (existingProxy) return existingProxy;

  // 创建 Proxy
  const proxy = new Proxy(target, baseHandlers);
  proxyMap.set(target, proxy);
  return proxy;
}

4. BaseHandlers (Proxy 拦截器)

getset 拦截器是响应式的核心。

typescript
/* baseHandlers.ts (Simplified) */
class BaseReactiveHandler implements ProxyHandler<object> {
  constructor(protected readonly _isReadonly = false) {}

  get(target: object, key: string | symbol, receiver: object): any {
    // 处理 Flags 访问 (isReactive, isReadonly, raw...)
    if (key === ReactiveFlags.IS_REACTIVE) return !this._isReadonly;
    
    // 获取结果
    const res = Reflect.get(target, key, receiver);

    // 收集依赖
    if (!this._isReadonly) {
      track(target, TrackOpTypes.GET, key);
    }
    
    // 递归响应式:如果属性是对象,且不是浅层响应,则继续 reactive 包装
    if (isObject(res)) {
      return this._isReadonly ? readonly(res) : reactive(res);
    }
    
    return res;
  }
}

5. 依赖收集 (Effect/Dep)

Vue 3 中没有 Watcher 类,而是使用 Effect(副作用)概念。

typescript
/* Dep.ts (Simplified) */
export class Dep {
  constructor() {}
  track() { /* ... */ }
  trigger() { /* ... */ }
}

// 全局依赖映射表:Target -> Key -> Dep
export const targetMap = new WeakMap();

export function track(target: object, type: TrackOpTypes, key: unknown) {
  // 核心逻辑:找到当前 target 的 depsMap,再找到 key 对应的 dep,调用 dep.track()
}

export function trigger(target: object, type: TrackOpTypes, key?: unknown) {
  // 核心逻辑:从 targetMap 取出 dep,调用 dep.trigger() 执行 effect
}

MIT Licensed | Keep Learning.