阅读视图

发现新文章,点击刷新页面。

大白话解释Vue响应式三要素

好的!咱们用最接地气的大白话聊聊 Vue 响应式系统的三个核心角色:Observer(观察者)、Watcher(监听者)、Dep(依赖收集器)。想象一下它们仨是怎么配合工作的: 🎯 场景比喻:班级通知

Vue组件变量值更新过程记录

从 Vue 2.x 源码角度分析将组件变量 a 从空值修改为 1 的完整调用栈如下:


1. 组件初始化阶段

在组件创建时,Vue 会初始化响应式数据:

// 调用栈:
Vue.prototype._init (init.js)
  └── initState (state.js)
      └── initData (state.js)
          └── observe (observer/index.js)
              └── new Observer (observer/index.js)
                  └── walk (observer/index.js)
                      └── defineReactive (observer/index.js) // 为属性 `a` 创建响应式

关键步骤

  • defineReactivea 创建 getter/setter
    • 初始化 dep 实例(依赖收集器)。
    • 通过 Object.defineProperty 重写 a 的访问器:
      Object.defineProperty(obj, key, {
        get() { /* 依赖收集 */ },
        set(newVal) { /* 触发更新 */ }
      })
      

2. 修改 a 的值

执行 this.a = 1 时触发 setter

// 调用栈:
this.a = 1
  └── a 的 setter (defineReactive 内部)
      └── dep.notify() (observer/dep.js)
          └── subs[i].update() (observer/watcher.js)
              └── queueWatcher (scheduler.js)
                  └── nextTick (scheduler.js)
                      └── flushSchedulerQueue (scheduler.js)
                          └── watcher.run (observer/watcher.js)
                              └── watcher.get (observer/watcher.js)
                                  └── 组件重新渲染 (render 函数)

关键步骤详解

  1. setter 触发

    • 检查新值 1 是否与旧值不同(newVal !== oldVal)。
    • 若不同,调用 dep.notify() 通知所有依赖。
  2. dep.notify()

    • 遍历 dep.subs(存储所有订阅该属性的 Watcher)。
    • 调用每个 watcher.update()
  3. watcher.update()

    • 将 Watcher 加入异步队列(queueWatcher)。
    • 通过 nextTick 异步执行更新。
  4. flushSchedulerQueue

    • 遍历队列中的 Watcher,调用 watcher.run()
    • watcher.run()watcher.get() → 重新执行组件的 render 函数。
  5. 重新渲染

    • render 函数执行时访问 a,触发 getter 重新收集依赖。
    • 生成新的虚拟 DOM,对比差异后更新真实 DOM。

3. 依赖收集机制

在首次渲染和后续更新时,getter 负责收集依赖:

// getter 调用栈:
组件访问 a (render 函数)
  └── a 的 getter (defineReactive 内部)
      └── Dep.target (全局唯一 Watcher)
          └── dep.depend() (observer/dep.js)
              └── 将当前 Watcher 添加到 dep.subs

关键点

  • Dep.target:全局唯一变量,指向当前正在执行的 Watcher(如渲染 Watcher)。
  • dep.depend():将当前 Watcher 加入 dep.subs,建立 属性 → Watcher 的依赖关系。

4. 异步更新队列

Vue 使用异步队列合并更新:

// nextTick 流程:
queueWatcher (scheduler.js)
  └── nextTick (util/next-tick.js)
      └── 异步任务 (Promise/MutationObserver/setTimeout)
          └── flushSchedulerQueue (scheduler.js)

优化逻辑

  • 多次修改 a 会被合并为一次更新(避免重复渲染)。
  • 通过 nextTick 确保在 DOM 更新后执行回调。

Vue 3 Proxy 版本的差异

若使用 Vue 3(基于 Proxy):

  1. 初始化:通过 reactive 创建响应式代理。
  2. 修改值:直接触发 Proxy.set 拦截器,后续流程类似(依赖收集、异步更新)。
  3. 核心差异
    • 无需 Object.defineProperty,支持动态属性。
    • 依赖收集通过 Track 操作,更新通过 Trigger 操作。

总结

阶段 核心操作 关键函数/类
初始化 a 创建响应式 getter/setter defineReactiveDep
修改值 触发 setter → 通知依赖 dep.notify()
依赖更新 异步队列合并更新 queueWatchernextTick
重新渲染 执行 render 函数 Watcher.run()

整个流程体现了 Vue 响应式系统的核心:依赖收集getter)和 派发更新setter),通过 异步队列 优化性能。

❌