Vue监听数据对象变化源码

监听数据对象变化,最容易想到的是建立一个需要监视对象的表,定时扫描其值,有变化,则执行相应操作,不过这种实现方式,性能是个问题,如果需要监视的数据量大的话,每扫描一次全部的对象,需要的时间很长。当然,有些框架是采用的这种方式,不过他们用非常巧妙的算法提升性能,这不在我们的讨论范围之类。

Vue 中数据对象的监视,是通过设置 ES5 的新特性(ES7 都快出来了,ES5 的东西倒也真称不得新)Object.defineProperty() 中的 set、get 来实现的。

目标

与官方文档第一个例子相似,不过也有简化,因为这篇只是介绍下数据对象的监听,不涉及文本解析,所以文本解析相关的直接舍弃了:

<div></div> var app = new Vue({ el: 'app', data: { message: 'Hello Vue!' } });

浏览器显示:

Hello Vue!

在控制台输入诸如:

app.message = 'Changed!'

之类的命令,浏览器显示内容会跟着修改。

Object.defineProperty

引用 MDN 上的定义:

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
与此相生相伴的还有一个 Object.getOwnPropertyDescriptor():

Object.getOwnPropertyDescriptor() 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

下面的例子用一种比较简单、直观的方式来设置 setter、getter:

var dep = []; function defineReactive(obj, key, val) { // 有自定义的 property,则用自定义的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() { var value = getter ? getter.call(obj) : val; dep.push(value); return value; }, set: function(newVal) { var value = getter ? getter.call(obj) : val; // set 值与原值相同,则不更新 if(newVal === value) { return; } if(setter) { setter.call(obj, newVal); } else { val = newVal; } console.log(dep); } }); }

var a = {}; defineReactive(a, 'a', 12); // 调用 getter,12 被压入 dep,此时 dep 值为 [12] a.a; // 调用 setter,输出 dep ([12]) a.a = 24; // 调用 getter,24 被压入 dep,此时 dep 值为 [12, 24] a.a;

Observer

简单说过 Object.defineProperty 之后,就要开始扯 Observer 了。observer,中文解释为“观察者”,观察什么东西呢?观察对象属性值的变化。故此,所谓 observer,就是给对象的所有属性加上 getter、setter,如果对象的属性还有属性,比如说 {a: {a: {a: 'a'}}},则通过递归给其属性的属性也加上 getter、setter:

function Observer(value) { this.value = value; this.walk(value); } Observer.prototype.walk = function(obj) { var keys = Object.keys(obj); for(var i = 0; i < keys.length; i++) { // 给所有属性添加 getter、setter defineReactive(obj, keys[i], obj[keys[i]]); } }; var dep = []; function defineReactive(obj, key, val) { // 有自定义的 property,则用自定义的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; // 递归的方式实现给属性的属性添加 getter、setter var childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() { var value = getter ? getter.call(obj) : val; dep.push(value); return value; }, set: function(newVal) { var value = getter ? getter.call(obj) : val; // set 值与原值相同,则不更新 if(newVal === value) { return; } if(setter) { setter.call(obj, newVal); } else { val = newVal; } // 给新赋值的属性值的属性添加 getter、setter childOb = observe(newVal); console.log(dep); } }); } function observe(value) { if(!value || typeof value !== 'object') { return; } return new Observer(value); }

Watcher

Observer 通过设置数据对象的 getter、setter 来达到监听数据变化的目的。数据被获取,被设置、被修改,都能监听到,且能做出相应的动作。

现在还有一个问题就是,谁让你监听的?

这个发出指令的就是 Watcher,只有 Watcher 获取数据才触发相应的操作;同样,修改数据时,也只执行 Watcher 相关操作。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwwxsj.html