返回

剖析Vue响应机制,Vue是如何侦测数据变化的?

前端

前言

Vue.js是一款流行的前端框架,以其简洁的语法和丰富的功能而受到广泛欢迎。Vue最独特的特性之一,是其非侵入性的响应式系统。它允许我们轻松地定义数据模型,并当数据发生变化时,自动更新视图。

本文将深入剖析Vue的响应式原理,以通俗易懂的方式讲解Vue如何侦测数据变化并更新视图。文章不仅涵盖了Vue2.x中的响应式原理,还提供了Vue2.x迷你版的代码实现,让读者能够更直观地理解Vue的响应式系统。

Vue响应式原理概述

Vue的响应式系统基于数据劫持和发布-订阅模式实现。当我们定义一个响应式数据对象时,Vue会劫持该对象的属性访问器和修改器,并在属性发生变化时触发对应的事件。订阅了这些事件的组件就会收到通知,并更新视图。

Vue响应式系统实现细节

数据劫持

Vue通过Object.defineProperty()方法劫持数据对象的属性访问器和修改器。当我们访问或修改一个响应式对象的属性时,实际上会触发劫持的访问器或修改器。在访问器中,Vue会收集订阅了该属性的组件。在修改器中,Vue会触发订阅了该属性的组件的更新。

发布-订阅模式

Vue使用发布-订阅模式来通知组件数据发生变化。当一个响应式对象的属性发生变化时,Vue会发布一个事件。订阅了该属性的组件就会收到通知,并更新视图。

Vue2.x迷你版实现

为了更直观地理解Vue的响应式原理,我们实现了一个Vue2.x迷你版。这个迷你版只实现了Vue响应式系统最核心的部分,但它足以让我们理解Vue的响应式原理。

// Vue2.x迷你版

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

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

  update() {
    let newValue = this.vm.$data[this.expr];
    let oldValue = this.value;
    if (newValue !== oldValue) {
      this.value = newValue;
      this.callback.call(this.vm, newValue, oldValue);
    }
  }
}

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

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

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

class Vue {
  constructor(options) {
    this.$data = options.data;
    this.$el = options.el;

    // 遍历data中的属性,将其转换为响应式
    this.observe(this.$data);

    // 编译模板
    this.compile(this.$el);
  }

  observe(data) {
    Object.keys(data).forEach((key) => {
      let dep = new Dep();
      Object.defineProperty(data, key, {
        get() {
          dep.addSub(Dep.target);
          return data[key];
        },
        set(newValue) {
          if (newValue !== data[key]) {
            data[key] = newValue;
            dep.notify();
          }
        },
      });
    });
  }

  compile(el) {
    // 省略编译过程,直接将指令绑定到元素上
    const nodes = el.querySelectorAll('[v-model]');
    nodes.forEach((node) => {
      const expr = node.getAttribute('v-model');
      const input = node.querySelector('input');
      const vm = this;
      input.addEventListener('input', (e) => {
        vm.$data[expr] = e.target.value;
      });

      new Watcher(vm, expr, (newValue) => {
        input.value = newValue;
      });
    });
  }
}

结语

本文深入剖析了Vue的响应式原理,并提供了一个Vue2.x迷你版的代码实现。通过本文,读者能够更直观地理解Vue的响应式系统是如何工作的。

Vue的响应式系统是一个非常巧妙的设计,它允许我们轻松地定义数据模型,并当数据发生变化时,自动更新视图。这使得Vue成为前端开发中非常流行的一个框架。