Vue 的关键概念与原理

检测对象变化

Vue 的核心功能之一是模板可以自动响应数据变化,这就涉及到如何检测数据的变动。

在版本 1 和 2 中,Vue 是基于 Object.defineProperty() 方法重写对象属性的 getter/setter 实现了对象属性变化的检测。Object.defineProperty() 是 ECMAScript5 标准添加的特性,无法通过 JavaScript 代码实现 polyfill(因此 Vue 无法用于像 IE8 这种不支持 ES5 的浏览器)。

而到了 Vue 3(发布于 2019 年),则采用了更新的 ES6 API —— Proxy。

Object.defineProperty()

我们先通过一段代码来看一下 Object.defineProperty() 是如何检测对象属性变化的。

/**
 * 通过这个函数,可以将传入的对象变成“响应式”的
 */
function makeObjectReactive(obj, func) {
  Object.keys(obj).forEach(key => {
    // val 变量被 getter/setter 闭包使用到,具有很长的生存期
    let val = obj[key];
    Object.defineProperty(obj, key, {
      get: function() {
        return val;
      },
      set: function(nextVal) {
        if (typeof func === 'function') {
          func(key, val, nextVal);
        }
        val = nextVal;
      }
    });
  });
  return obj;
}

var a = makeObjectReactive({
  name: 'Hello',
  age: 13,
}, (key, val, nextVal) => {
  console.log(`对象属性 ${key} 发生变化: ${val} => ${nextVal}`);
});

// 我们在修改对象 a 的两个属性时,总是能够自动调用我们传入的函数(makeObjectReactive 的第二个参数)
a.name = 'World';  // 对象属性 name 发生变化: Hello => World
a.age  = 15;       // 对象属性 age 发生变化: 13 => 15

Vue.js 的处理

var vm = new Vue({
  data: {
    name: '',
  }
});

构造函数 Vue 实际上会遍历 data 下的所有属性,重新设置 gettersetter