經過昨天佛心公司洗禮後,發現自己對雙向綁定與 Virtual Dom 回答的還是五五六六的,因為只回答了 Vue 的雙向綁定原理是由 Object.defineProperty 去做資料劫持,被反問說既然你知道用 Object.defineProperty ,那這個有什麼缺點或限制嗎 ? 當下發現原來自己根本還只是在 Vue 皮毛階段,往下問就掛了,決定來惡補一下。
年初寫的文章結果還是忘了一半QQ
雙向綁定
首先 Vue 雙向綁定在 3.0 之前都是使用 Object.defineProperty
Object.defineProperty 可直接修改、或定義現有屬性,提供三個參數
- 要定義或修改的 object
- 要定義或修改的屬性 key
- 目標屬性要擁有的特性
const object1 = {};Object.defineProperty(object1, 'property1', {
value: 42,
});object1.property1 = 77;
// throws an error in strict modeconsole.log(object1.property1);
// expected output: 42
第三點分為以下這幾種特性
- value (值)
- writable (可不可寫入)
- configurable (能不能被設定,預設false,如果第一次 define沒設定 true 的話則第二次將報錯)
- enumerable (能不能被 for…in 列舉)
- get (取值觸發此 function)
- set (賦值觸發此 function)
注意其中 get, set 無法與 value, writable 共用不然會報錯
let obj = {}Object.defineProperty(obj, 'a', {
value: '還我急急三比零',
get: function() { return 0xdeadbeef; }
});
// Error: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
所以當我們需要綁定 view 與 資料我們就能用 get, set 去做響應
var userInfo = {};
Object.defineProperty(userInfo, "nickName", {
get: function(){
return document.getElementById('nickName').innerHTML;
},
set: function(nick){
document.getElementById('nickName').innerHTML = nick;
}
});
當然這只是 Vue 響應式其中一小部分,更複雜的 get, set 後面還有 watcher 到directive 到改變 dom 結構,詳細在官網上就不偏題了
回到問題,那為何 Object.defineProperty 有限制呢?
- 在 Vue 中,無法監聽陣列變化
這個問題可以參考下面這篇回答,其實 Object.defineProperty 這方法是可以對陣列操作的,但是效能問題導致 Vue 沒有把這功能加入
2. 只能劫持物件屬性,如果物件有更深一層物件那就要在深度遍歷一次了,這造成很大的效能問題
因此在 Vue3.0 中,用 ES6 的 Proxy 取代了Object.defineProperty,可以看看以下投影片尤大介紹 Vue3.0
Proxy
Proxy 意思是代理,能夠用此對物件進行攔截
var proxy = new Proxy(target, handler);
- target 為要 proxy 的物件
- handler function 可以在此定義 get, set
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
Reflect 屬性
所以 Vue 用此來代替 Object.defineProperty 方法來對物件 get, set,他能夠對整個物件進行劫持並且返回一個新的對象
Proxy 進行雙向綁定
總結
- 在 Vue3.0 前使用Object.defineProperty 做雙向綁定資料劫持
- Vue3.0後使用 Proxy 代理(攔截) 物件的 get, set