Object.defineProperty
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
const proxy = function(target,key){
let value ;
Object.defineProperty(target,key,{
set(val){
value = val;
console.info('set')
},
get(){
console.info('get')
return value;
}
})
}
const obj = Object.create(null);
proxy(obj,'arr');
obj.arr = [];
打印出
'set'
在Vue中的应用
众所周知,Vue的数据更新触发页面渲染,采用的就是Object.defineProperty 中对对象属性set的监听。
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的实例初始化时,将组件的初始化数据,逐级实例成可观察的对象。
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (Array.isArray(value)) {
var augment = hasProto
? protoAugment
: copyAugment;
augment(value, arrayMethods, arrayKeys);
this.observeArray(value);
} else {
this.walk(value);
}
};
Object.defineProperty 存在的问题
-
如果添加的属性值是数组,当使用push等方法改变数组,不会触发set
const obj = Object.create(null); proxy(obj,'arr'); obj.arr = []; obj.arr.push(1);
不会打印出
'set'
-
如果添加的属性值是多层级的对象,对于深层级的修改,不会触发到set
// 紧跟前面例子 proxy(obj,'deepObj'); obj.deepObj = {a:{b:c:1}} obj.deepObj.a.b.c = 2
不会打印出
'set'
Vue针对存在问题的处理
-
对于数组的更新问题
Vue重写Array原型链上的几个方法,使得当使用push,pop,shift,unshift,splice,sort,reverse这几个方法时,也能触发页面刷新。var arrayProto = Array.prototype; var arrayMethods = Object.create(arrayProto);[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ] .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 }); });
-
针对多层级对象,如果没有在data定义,直接修改值,是不会触发页面更新的。
var vm = new Vue({ data(){ return { a: 1 } ; } }) vm.a = 2; //会触发页面刷新 vm.b = 3; //不会触发页面刷新
此时,如果想更改没有定义属性,需要使用官方推荐的实例$set方法。
vm.$set(vm.data, 'b', 3) //会触发页面刷新
大概了解了Vue的数据更新原理,就可以开始联想Vue3.0的Proxy啦~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。