小陈博客-个人分享

《Vue学习》数据响应式原理

数据响应式

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

资源介绍

语法

Object.defineProperty(obj, prop, descriptor)

先看看基础函数

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

const object1 = {};

Object.defineProperty(object1, "property1", {
  value: 42,
  writable: false,
});

object1.property1 = 77;
// 默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。

console.log(object1.property1);
// expected output: 42

[tabs]

[tab title="共享描述符"]

[tab title="数据描述符"]

[tab title="存取描述符"]

[tab title="描述符默认值汇总"]

[tab title="描述符可拥有的键值"]

configurableenumerablevaluewritablegetset
数据描述符可以可以可以可以不可以不可以
存取描述符可以可以不可以不可以可以可以

如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。
[/tab]

[/tabs]

实践

<div id="app">hello</div>
// 模拟 Vue 中的 data 选项
let data = {
  msg: "hello",
};

// 模拟 Vue 的实例
let vm = {};

// 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
Object.defineProperty(vm, "msg", {
  enumerable: true,
  configurable: true,
  get() {
    console.log("get: ", data.msg);
    return data.msg;
  },
  set(newValue) {
    console.log("set: ", newValue);
    if (newValue === data.msg) {
      return;
    }
    data.msg = newValue;
    // 数据更改,更新 DOM 的值
    document.querySelector("#app").textContent = data.msg;
  },
});

vm.msg = "Hello World";
console.log(vm.msg);

// set:  Hello World
// get:  Hello World
// Hello World

那假设是有多属性的对象呢?需要如何绑定。

核心思路就是递归遍历对象,将对象内的所有属性都绑定。

let data = {
  msg: "hello",
  age: 18,
};

let vm = {};

proxyData(data);

function proxyData(data) {
  Object.keys(data).forEach((key) => {
    // 把 data 中的属性,转换成 vm 的 setter/setter
    Object.defineProperty(vm, key, {
      enumerable: true,
      configurable: true,
      get() {
        console.log("get: ", key, data[key]);
        return data[key];
      },
      set(newValue) {
        console.log("set: ", key, newValue);
        if (newValue === data[key]) {
          return;
        }
        data[key] = newValue;
        // 数据更改,更新 DOM 的值
        document.querySelector("#app").textContent = data[key];
      },
    });
  });
}

简单了解数据劫持后,我们来看看完整流程。Vue 初始的时候,将 data 中的数据通过 Observe 数据劫持 后,将数据利用观察者模式来监控,当事件触发时,通过 发布者Dep 去调用 观察者Watcher 的方法来更新视图。

了解大致流程后,我们从基础出发,理解 Vue 中的设计模式: 发布/订阅模式观察者模式。后面会针对这两点继续深入。

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »