返回

Vue 2 数据响应式原理详解,揭开数据监听的奥秘

前端

导言

Vue.js 作为一种流行的 JavaScript 框架,以其响应式数据管理而著称。在本文中,我们将深入探讨 Vue 2 的数据响应式原理,手把手地带你揭开数据监听的奥秘。

观察者模式与响应式

Vue 的响应式性是建立在观察者模式之上的。观察者模式是一种设计模式,允许对象(观察者)订阅其他对象(主题)的状态变化。当主题的状态发生变化时,它会通知所有订阅者,从而触发相应的动作。

在 Vue 中,数据对象扮演主题的角色,而 Vue 实例则扮演观察者的角色。当数据对象发生变化时,Vue 会自动检测到这些变化并触发更新视图。

数据劫持

为了实现响应式性,Vue 采用了一种名为数据劫持的技术。当 Vue 实例创建时,它会遍历数据对象并使用 getter 和 setter 来劫持对象的每个属性。

  • getter: 当读取数据对象属性时,getter 会被调用。它返回属性的值,同时将 Vue 实例添加为该属性的依赖项。
  • setter: 当写入数据对象属性时,setter 会被调用。它更新属性值,同时通知所有依赖项进行更新。

通过这种方式,Vue 可以跟踪数据对象属性的变化并相应地更新视图。

依赖收集

当数据对象属性被读取时,getter 会将 Vue 实例添加为该属性的依赖项。当属性发生变化时,setter 会通知所有依赖项进行更新。

Vue 使用依赖收集来优化更新过程。当属性发生变化时,只有与该属性相关联的组件和视图才会被更新,而不是整个应用程序。

手写简版 Vue 2 数据响应式

为了加深对 Vue 2 数据响应式原理的理解,我们来手动实现一个简版的 Vue 2 数据响应式。

数据劫持:

function observe(obj) {
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      defineReactive(obj, key, obj[key]);
    }
  }
}

function defineReactive(obj, key, value) {
  let dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      dep.addSub(Dep.target);
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        value = newValue;
        dep.notify();
      }
    },
  });
}

依赖收集:

class Dep {
  constructor() {
    this.subs = [];
  }

  addSub(sub) {
    this.subs.push(sub);
  }

  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

Dep.target = null;

更新视图:

class Watcher {
  constructor(vm, expr, cb) {
    this.vm = vm;
    this.expr = expr;
    this.cb = cb;
    this.value = this.get();
  }

  get() {
    Dep.target = this;
    const value = this.vm.data[this.expr];
    Dep.target = null;
    return value;
  }

  update() {
    const newValue = this.get();
    if (newValue !== this.value) {
      this.value = newValue;
      this.cb(newValue);
    }
  }
}

结语

通过深入剖析 Vue 2 的数据响应式原理,我们不仅掌握了其工作机制,更体会到观察者模式和数据劫持在实现响应式中的关键作用。理解这些原理对于编写高效且可维护的 Vue 应用程序至关重要。