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

响应式数据与依赖追踪

Vue通过 Observer 递归观测对象和数组,Dep 收集依赖,Watcher 观察更新,实现数据驱动视图更新。

Observer 观测器

JavaScript
class Observer {
  value: any
  dep: Dep
  vmCount: number
  
  constructor(value: any) {
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)  // 挂载__ob__引用
    
    if (Array.isArray(value)) {
      // 数组: 重写原型方法
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      // 对象: 遍历属性定义响应式
      this.walk(value)
    }
  }
  
  walk(obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
  
  observeArray(items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

Observer是观测入口,对象遍历属性定义响应式,数组重写原型方法。

defineReactive 响应式定义

JavaScript
function defineReactive(obj, key, val, customSetter, shallow) {
  const dep = new Dep()
  let childOb = !shallow && observe(val)
  
  Object.defineProperty(obj, key, {
    get() {
      const value = val
      if (Dep.target) {
        dep.depend()           // 收集当前Watcher
        if (childOb) {
          childOb.dep.depend() // 收集数组/对象Observer
        }
      }
      return value
    },
    set(newVal) {
      if (newVal === val) return
      val = newVal
      childOb = !shallow && observe(newVal)
      dep.notify()             // 通知所有Watcher更新
    }
  })
}

getter收集依赖,setter派发更新,新值递归观测。

Dep依赖管理

JavaScript
class Dep {
  static target: ?Watcher = null
  id: number
  subs: Array<Watcher>
  
  constructor() {
    this.id = uid++
    this.subs = []
  }
  
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  
  notify() {
    const subs = this.subs.slice()
    for (let i = 0; i < subs.length; i++) {
      subs[i].update()
    }
  }
}

每个属性独立Dep,Dep.target 全局指向当前活跃Watcher。

Watcher观察者

JavaScript
class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm
    this.cb = cb
    this.deps = []
    this.depIds = new Set()
    this.getter = parsePath(expOrFn)
    this.value = this.get()
  }
  
  get() {
    pushTarget(this)    // 设置Dep.target
    value = this.getter.call(this.vm, this.vm)
    return value
  }
  
  addDep(dep: Dep) {
    if (!this.depIds.has(dep.id)) {
      this.depIds.add(dep.id)
      this.deps.push(dep)
      dep.addSub(this)
    }
  }
  
  update() {
    queueWatcher(this)  // 异步批量更新
  }
}

Watcher通过 get() 触发依赖收集,update() 进入更新队列。

依赖收集流程

JavaScript
// 1. 组件挂载
mountComponent(vm) {
  new Watcher(vm, () => {
    vm._update(vm._render())  // 渲染函数
  })
}

// 2. Watcher.get() 执行
pushTarget(watcher)  // Dep.target = watcher
vm._render()         // 执行渲染
  -> _c('div')       // 访问data
    -> data.msg的getter
      -> dep.depend()  // 收集watcher
      -> childOb.dep.depend()  // 收集数组Observer

// 3. 数据变化
this.msg = 'new'
  -> msg的setter
    -> dep.notify()
      -> watcher.update()
        -> queueWatcher(watcher)

渲染时访问数据触发getter收集Watcher,数据变化触发setter派发更新。

要点总结

  • Observer 是观测入口,对象遍历属性、数组重写原型方法
  • defineReactive 劫持getter/setter,每个属性独立Dep
  • getter收集 Dep.target 指向的Watcher,setter派发更新
  • Dep 管理属性订阅的Watcher数组,使用副本遍历
  • Watcher 通过 get() 触发依赖收集,update() 进入更新队列
  • 新值递归观测,确保深层数据变化也能触发更新

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

← 上一篇 Diff算法与Patch过程
下一篇 → 异步更新队列与nextTick
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

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

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