Vue3 响应式系统实现

手写 Vue3 简易响应式,包含 Proxy、reactive 和 effect

问题

从零实现 Vue3 的响应式系统,包括 reactiveeffect 函数。

解答

依赖收集系统

// 当前正在执行的 effect
let activeEffect = null

// 存储依赖关系:WeakMap<target, Map<key, Set<effect>>>
const targetMap = new WeakMap()

// 收集依赖
function track(target, key) {
  if (!activeEffect) return
  
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  
  let deps = depsMap.get(key)
  if (!deps) {
    deps = new Set()
    depsMap.set(key, deps)
  }
  
  deps.add(activeEffect)
}

// 触发更新
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  
  const deps = depsMap.get(key)
  if (deps) {
    deps.forEach(effect => effect())
  }
}

实现 reactive

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver)
      // 收集依赖
      track(target, key)
      // 如果是对象,递归代理
      if (typeof result === 'object' && result !== null) {
        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
    }
  })
}

实现 effect

function effect(fn) {
  const effectFn = () => {
    activeEffect = effectFn
    fn()
    activeEffect = null
  }
  // 立即执行一次,收集依赖
  effectFn()
  return effectFn
}

测试代码

const state = reactive({
  count: 0,
  user: {
    name: 'Tom'
  }
})

// 注册副作用
effect(() => {
  console.log('count:', state.count)
})

effect(() => {
  console.log('name:', state.user.name)
})

// 修改数据,自动触发更新
state.count++        // 输出: count: 1
state.user.name = 'Jerry'  // 输出: name: Jerry

完整代码

let activeEffect = null
const targetMap = new WeakMap()

function track(target, key) {
  if (!activeEffect) return
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  let deps = depsMap.get(key)
  if (!deps) {
    deps = new Set()
    depsMap.set(key, deps)
  }
  deps.add(activeEffect)
}

function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const deps = depsMap.get(key)
  if (deps) {
    deps.forEach(effect => effect())
  }
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver)
      track(target, key)
      if (typeof result === 'object' && result !== null) {
        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
    }
  })
}

function effect(fn) {
  const effectFn = () => {
    activeEffect = effectFn
    fn()
    activeEffect = null
  }
  effectFn()
  return effectFn
}

关键点

  • WeakMap 存储依赖targetMap -> depsMap -> deps 三层结构,WeakMap 不阻止垃圾回收
  • Proxy 拦截操作:get 时收集依赖,set 时触发更新
  • Reflect 配合 Proxy:保证 this 指向正确,处理继承场景
  • activeEffect 标记:用全局变量记录当前执行的 effect,实现自动依赖收集
  • 嵌套对象处理:get 时递归调用 reactive,实现深层响应式