Vue2的數據響應式原理
1、什么是defineProperty?
defineProperty是設置對象屬性,利用屬性里的set和get實現了響應式雙向綁定;
語法:Object.defineProperty(要設置的對象,要修改的對象的屬性,屬性描述)
屬性描述:
1、configurable - 表示此屬性能否被delete,默認false;
2、enumerable - 表示此屬性能否被枚舉,默認為false;
3、value - 設置此屬性對應的值,默認為undefined;
4、writable - 設置value屬性能否被修改值,為true時方可被改變,默認為false;
5、get - 給屬性提供getter方法,默認為undefined,訪問該屬性時,該方法會被執行,默認參數為this對象;
6、set - 給屬性提供setter方法,默認為undefined,屬性值修改時,會執行該方法,唯一參數為新的值;
獲取對象所擁有屬性的方法:Object.getOwnPropertyDescriptor(對象,對象某一屬性)
var obj = {
a: 1,
b: 2
}
var value = obj.b;
Object.defineProperty(obj,'b',{
configurable: false,//不能被deleted
enumerable: false,//不能被刪除
writable: true,//可以被修改值
value: '22',//設置b為22
set: function(newValue){
console.log('新的value為'+ newValue);
value = newValue;
},
get: function(){
console.log('你設置了value');
return value;
},
})
console.log(Object.getOwnPropertyDescriptor(obj,'a')); //{value: 1, writable: true, enumerable: true, configurable: true}
2、實現雙向綁定:
function vue(){
this.$data = {a: 1};//設置data
this.el = document.getElementById('app'); //設置根節點
this.dom = '';//虛擬dom
}
//往原型上綁定監聽方法
vue.prototype.observe = function(obj){
var self = this;
var value;
for(var key in obj){
value = obj[key];
if(typeof value === 'object'){ //如果是對象 需要遞歸循環執行此方法
this.observe(value);
}else{
Object.defineProperty(this.$data,key,{
get: function(){
//依賴收集 這里略過 代替源碼里的 depend
return value;
},
set: function(newVal){
value = newVal;
self.render();//觸發更新 代替源碼里的dep.notify
}
})
}
}
}
vue.prototype.render = function(){
this.dom = '我是' + this.$data.a;
this.el.innerHTML = this.dom;
}
3、數組監聽實現
vue數組的特性: push shift unshift
對象的監聽是通過defineProperty,而數組是通過dependArray
Object.create(proto,propertiesObject) //方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__
var arrPro = Array.prototype;
var arrObj = Object.create(arrPro);
var arr = ['push','pop','shift'];
//裝飾者模式
arr.forEach(function(method,index){
arrObj[method]=function(){
var ret = arrPro[method].apply(this,arguments);
dep.notify();
return ret;
}
})
Vue3的數據相應原理
let p = new Proxy(target, handler);
target: 目標對象
handler: 是一個對象,其屬性是當執行一個操作時定義代理的行為的函數。
handler中常用的對象方法如下:
receiver => 此Proxy對象本身,也就是this指向;
1. get(target, propKey, receiver)
2. set(target, propKey, value, receiver)
3. has(target, propKey)
4. construct(target, args)
5. apply(target, object, args)
寫法如下:
let a = new Proxy({},{
get: function(target,key,receiver){
return Reflect.get(target[key]);
//return target[key];
},
set: function(target, key, value, receiver){
return Reflect.set(target,key,value);
//return target[key] = value;
}
})
Object.defineProperty 存在如下缺點:
1. 監聽數組的方法不能觸發Object.defineProperty方法中的set操作(如果要監聽的到話,需要重新編寫數組的方法)。
2. 必須遍歷每個對象的每個屬性,如果對象嵌套很深的話,需要使用遞歸調用。
Proxy相比defineProperty 區別:
1、參數不一樣,defineProperty第二個參數需要指定操作對象的具體key值,而Proxy指定的是最外層對象,這樣就省去了循環;
2、defineProperty操作的是對象本身,改變了對象本身,而Proxy是改變對象代理,返回新對象;
3、defineProperty必須在最外層定義一個全局變量value,通過get將其return出去,而Proxy將返回值放到了參數中,直接return target[key];
Diff算法和virtual dom
注:virtual dom是一個虛擬層,並不正式存在
diff算法是直接去比對元素,元素里包含props 和 children, 一直進行到根節點,template中所有的節點都會有個diff,掛載了所綁定的一系列屬性,通過遍歷循環監聽到變化,在vue3中作者改變了這種方式,在發布會中講到對比vue2速度提升了6倍,
vue2的虛擬dom,會遍歷所有子節點,全部用diff算法比對一次,遍歷速度由dom數量決定;
vue3中會找出變化的項,只更新改變項;
