Vue从入门到精通之响应式原理
2019年2月25日
前言
对 Vue
有点了解的人都知道 Vue
的数据双向绑定,是其非常重要的特性之一。那么 Vue
是如何进行数据更新的了?又是如何追踪数据变化的了?本篇文章就以该问题为主题来聊聊 Vue
的数据响应式原理!
追踪对象类型的数据变化
关于 Vue
如何追踪数据变化在 Vue官方文档-深入响应式原理 中有详细的说明。
其实简单来说,Vue
就是通过使用 ES5
中的 Object.defineProperty
将所有绑定在 data
选项中的属性全部都转为 getter/setter
。然后在 setter
中添加监控方法,当每次属性被改变时,触发改监控方法,以达到通知 watcher
重新计算及通知相关数据更新的目的!
由于使用的是 ES5
中的 Object.defineProperty
方法,这就导致 Vue
无法支持 IE8
及以下版本的浏览器。
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
看过以上内容,相信大家对于对象类型的数据追踪应该有了一些初步的了解,这也就很容易理解接下来要讲的内容了。
上面我们说了,Vue
是通过将属性转为 getter/setter
来实现数据变化检测的。那么,这就要求在实例初始化时,这些属性必须已经在 data
对象上才能实现。这也就导致,Vue
无法检测到对象属性的动态添加或删除!如果需要动态的添加 data
上的相应式属性,可以使用 Vue.set(object, key, value)
实现!
追踪数组类型的数据变化
上面的内容大家就算没有看过,但是多少应该都知道一点。但是,不知道大家有没有想过:只有对象类型的数据才能使用 getter/setter
来监控数据变化。那么如果数据是数组类型的数据了,怎么进行数据监测了?
有人可能会说,数组也是对象啊,哈哈。原则上这种说法没有错,但是数组可没法定义 getter/setter
。那么 Vue
是如何实现对于数组类型数据进行监控的了,详情看源码:
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
var original = arrayProto[method];
def(arrayMethods, method, function mutator () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
var result = original.apply(this, args);
var ob = this.__ob__;
var inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break
case 'splice':
inserted = args.slice(2);
break
}
if (inserted) { ob.observeArray(inserted); }
// notify change
ob.dep.notify();
return result
});
});
大家看完源码后,是不是就明白了!是的,没错, Vue
中重写了数组常用的 push
, pop
, shift
, unshift
, splice
, sort
, reverse
方法!并且在这些方法中添加了数据监控钩子!当数组使用这些方法,进行数据变化时,就会触发监控函数,从而达到监控数组数据变化的目的!
但是,很明显的,Vue
只是重写了以上这些方法,那么,当我们使用索引的方式动态添加,或改变数组数据,Vue
就无法监控数据变化了:
data() {
arr: [1, 2, 3, 4],
},
mouted() {
// 动态添加元素
this.arr[4] = 5;
// 动态修改元素
this.arr[1] = 6;
}
所以,在日常使用中,我们要尽量避免使用数组索引的方式去改变数据,如果有需要的话,可以使用 vue.set
方法进行数据修改!
结尾
到这里,文章基本也就结束,本文主要是简单分析了一下 Vue
中的数据监控,当然主要部分还是数组部分的数据监控实现,毕竟对于对象的 getter/setter
知道的人还是比较多的,但是对于数组数据的监控方法实现,知道的人就相对少一点了!
转载说明
本文允许全文转载,转载请注明来源: 平凡公子 - vue从入门到精通之响应式原理