Vue.js 中的响应式奥秘:探索 Watcher类

137 阅读4分钟

最近面试发现,使用Vue.js进行开发的团队似乎已经远超过了react了。因此学习总结了一下vue的观察者模式。Vue的响应式系统让开发者可以自如地实现数据与视图的同步。那么,Vue 是如何将这种响应式带到我们的应用中的呢?本文将深入探讨 Vue 中的 Watcher类,以及它如何与渲染函数和响应式系统相互作用,实现视图更新。

前置知识:观察者与依赖

在 Vue 中,响应式系统的核心是观察者(Watcher)和依赖(Dep)。简单来说,Watcher实例是一个观察者,它用于监视数据的变化,而Deps实例是一个依赖容器,用于存储与某个数据属性关联的一系列 Watcher实例。当数据发生变化时,Dep 会通知所有关联的 Watcher,从而触发回调函数并执行相应的操作(如更新视图)。

Vue 渲染函数

当我们在 Vue 中编写模板时,Vue 实际上会将这些模板编译成渲染函数。渲染函数是一个 JavaScript 函数,它会根据组件的数据生成相应的虚拟 DOM。在执行渲染函数的过程中,Vue 会访问我们在模板中绑定的数据属性。这些数据属性的访问触发了响应式系统的 getter,从而进行依赖收集。

举个例子,假设我们有以下模板:

<div id="app">
  <p>{{ message }}</p>
</div>

Vue 会将此模板编译成如下渲染函数:

function render() {
  with (this) {
    return _c('div', { attrs: { id: 'app' } }, [
      _c('p', [_v(_s(message))]),
    ]);
  }
}

万物皆可 Watch:挂载时的响应式

在 Vue 组件实例创建过程中的挂载阶段,Vue 会创建一个渲染 Watcher 实例,负责监听整个组件的数据变化并重新渲染。当执行渲染函数时,Watcher实例会被设置为当前活动的 Watcher(Dep.target)。

随着渲染函数的执行,所有在模板中绑定的数据属性都会被访问。访问这些属性时,会触发响应式系统的 getter,从而将 Watcher实例 添加到相应属性的依赖(Dep)列表中。

让我们回顾一下 defineReactive 函数中的 getter:

get: function reactiveGetter() {
    const dep = new Dep();
    const value = getter ? getter.call(obj) : obj[key];
    if (Dep.target) {
        dep.depend();
        if (childOb) {
            childOb.dep.depend();
        }
    }
    return value;
}

在执行渲染函数期间,访问数据属性的操作实际上建立了数据属性与 Watcher实例 之间的联系。这样,在数据属性发生变化时,这些 Watcher 实例会收到通知并重新计算渲染函数,从而实现视图的响应式更新。

触发更新:当数据变化时

当我们的数据属性发生变化时,响应式系统的 setter 会被触发。setter 会通知与该属性关联的依赖(Dep)列表中的所有 Watcher 实例。

在收到通知后,Watcher 会执行回调函数(在 mountComponent 函数中定义),重新计算渲染函数并生成新的虚拟 DOM。接下来,Vue 使用虚拟 DOM diffing 算法来比较新旧虚拟 DOM,并计算出需要进行的 DOM 更新。然后,Vue 将这些更新应用到实际的 DOM,从而实现视图的响应式更新。

让我们看一下简化版的 mountComponent 函数:

function mountComponent(vm, el) {
  // 创建一个渲染 Watcher 实例
  const updateComponent = () => {
    vm._update(vm._render());
  };

  new Watcher(vm, updateComponent);
}

总结:响应式的魔法盒子

现在让我们回顾一下 Vue 的响应式奥秘是如何在幕后运作的:

  1. Vue 将模板编译成渲染函数。
  2. 在挂载阶段,Vue 为组件创建一个 Watcher 实例,监听整个组件的数据变化。
  3. 执行渲染函数时,访问模板中绑定的数据属性,从而触发响应式系统的 getter 并将 Watcher 添加到相应属性的依赖(Dep)列表中。
  4. 当数据属性发生变化,触发响应式系统的 setter,并通知所有关联的 Watcher。
  5. Watcher 收到通知,重新计算渲染函数并更新虚拟 DOM。
  6. Vue 使用虚拟 DOM diffing 算法计算 DOM 更新,然后将更新应用到实际的 DOM。

通过这个过程,Vue 能够实现数据与视图之间的响应式同步,为开发者带来更加便捷的开发体验。