全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

响应式系统与依赖追踪

Vue响应式基于 Object.defineProperty 劫持getter/setter,通过 Dep 类实现依赖收集与派发更新。

defineReactive 核心实现

JavaScript
function defineReactive(obj, key, val, customSetter, shallow) {
  const dep = new Dep()  // 每个属性独立Dep
  
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) return
  
  const getter = property && property.get
  const setter = property && property.set
  
  // 递归观测子对象
  let childOb = !shallow && observe(val)
  
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val
      
      // 依赖收集
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      
      return value
    },
    set: function reactiveSetter(newVal) {
      const value = getter ? getter.call(obj) : val
      if (newVal === value || (newVal !== newVal && value !== value)) return
      
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      
      // 新值观测
      childOb = !shallow && observe(newVal)
      
      // 派发更新
      dep.notify()
    }
  })
}

getter收集依赖,setter派发更新,每个属性有独立 Dep 实例。

Dep 依赖管理类

JavaScript
let uid = 0

class Dep {
  constructor() {
    this.id = uid++
    this.subs = []  // Watcher数组
  }
  
  // 添加订阅者
  addSub(sub: Watcher) {
    this.subs.push(sub)
  }
  
  // 移除订阅者
  removeSub(sub: Watcher) {
    remove(this.subs, sub)
  }
  
  // 收集依赖
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  
  // 派发更新
  notify() {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

Dep.target = null  // 全局唯一活跃Watcher

Dep 管理属性的所有订阅Watcher,使用副本防止遍历时修改数组。

Watcher 观察者

JavaScript
class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm
    this.cb = cb
    this.deps = []        // 依赖的Dep列表
    this.depIds = new Set()
    this.getter = parsePath(expOrFn)  // 解析表达式
    this.value = this.get()
  }
  
  get() {
    pushTarget(this)  // 设置Dep.target
    const vm = this.vm
    value = this.getter.call(vm, vm)  // 执行函数触发getter
    return value
  }
  
  addDep(dep: Dep) {
    const id = dep.id
    if (!this.depIds.has(id)) {
      this.depIds.add(id)
      this.deps.push(dep)
      dep.addSub(this)
    }
  }
  
  update() {
    if (this.lazy) {
      this.dirty = true  // 计算属性懒更新
    } else if (this.sync) {
      this.run()  // 同步执行
    } else {
      queueWatcher(this)  // 异步批量更新
    }
  }
}

Watcher通过 get() 触发getter收集依赖,update() 派发更新。

依赖收集过程

JavaScript
// 组件渲染Watcher
mountComponent(vm) {
  updateComponent = () => {
    vm._update(vm._render())
  }
  new Watcher(vm, updateComponent, noop)
}

// Watcher.get() 执行时
pushTarget(this)  // Dep.target = this.watcher
vm._render()      // 执行渲染函数
  -> _c('div')    // 访问data.msg
    -> msg的getter
      -> dep.depend()  // 收集当前Watcher

渲染函数执行时访问响应式数据,触发getter收集渲染Watcher。

数组响应式处理

JavaScript
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push', 'pop', 'shift', 'unshift',
  'splice', 'sort', 'reverse'
]

methodsToPatch.forEach(function(method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function(...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    
    // 新元素观测
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    
    // 通知更新
    ob.dep.notify()
    return result
  })
})

数组通过拦截7个变异方法,新元素观测和派发更新。

要点总结

  • defineReactive 劫持getter/setter,每个属性独立 Dep 实例
  • getter收集 Dep.target 指向的当前Watcher,setter派发更新
  • Dep 管理属性订阅的Watcher数组,使用副本遍历防止修改
  • Watcher 通过 get() 触发依赖收集,update() 派发更新
  • 数组通过拦截 push/pop/shift/unshift/splice/sort/reverse 实现响应式
  • 计算属性使用 lazy 标记,更新时只设 dirty 不立即执行

📝 发现内容有误?点击此处直接编辑

← 上一篇 事件系统与指令处理底层实现
下一篇 → 插槽与作用域插槽底层实现
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库