雙向綁定的方法
- KnockoutJS 基於觀察者模式(
發佈-訂閱
)的雙向綁定 - Ember 基於
數據模型
的雙向綁定 - Angular 基於
髒檢查
的雙向綁定 - 基於數據劫持的雙向綁定 (重點講解)
Object.defineProperty
Proxy
數據劫持
數據劫持的優點
-
不需要顯示的調用
比如:Vue 利用數據劫持+發佈訂閱,可以直接通知變化並且驅動視圖
-
可以精確得知變化數據
我們劫持了屬性
setter
,當屬性值改變我們可以精確的獲取變化的內容newVal
,不需要額外的diff
操作
數據劫持雙向綁定的實現思路
- 利用
Proxy
或Object.defineProperty
生成的Observer
針對對象/對象的屬性進行劫持,在屬性發生變化時通知訂閱者 - 解析器
Compile
會解析模板中的指令,收集指令依賴的數據和方法,等待數據變化,然後進行渲染 Watcher
接收Observer
產生的數據變化,根據Compile
提供的指令進行視圖渲染,使得數據變化促使視圖更新
基於 Object.defineProperty 雙向綁定的特點
利用 Object.defineProperty
方法劫持對象的訪問器,在 屬性值
發生變化的時候獲取到變化,並且可以進一步操作。
// 這是將要被劫持的對象
const data = {
name: ""
};
function say(name) {
switch (name) {
case "a":
console.log("my name is a");
break;
case "b":
console.log("my name is b");
break;
case "c":
console.log("my name is c");
break;
default:
console.log("my name is none");
break;
}
}
// 遍歷對象,對其屬性值進行劫持
Object.keys(data).forEach(function(key) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
console.log("get");
},
set: function(newVal) {
// 當屬性值發生變化時我們可以進行額外操作
console.log(`大家好,我係${newVal}`);
say(newVal);
}
});
});
data.name = "a";
//大家好,我係a
//my name is a
案例地址:https://codepen.io/xiaomuzhu/pen/jxBRgj/?editors=1010
基於 Proxy 實現的雙向綁定的特點
const input = document.getElementById("input");
const p = document.getElementById("span");
const obj = {};
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(target, key, value, receiver);
// 第一次打印:{} "text" "f" Proxy {}
// 第二次打印:{text: "f"} "text" "ff" Proxy {text: "f"}
if (key === "text") {
input.value = value;
p.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
}
});
input.addEventListener("keyup", function(e) {
newObj.text = e.target.value;
});
案例地址:https://codepen.io/xiaomuzhu/pen/zjwGoN/
對比兩種雙向綁定
Object.defineProperty 的缺陷
-
無法監聽數組變化
Vue 的文檔提到了 Vue 是可以檢測到數組變化的,但是隻有以下八種方法:
push pop shift unshift splice sort reverse
,vm.items[indexOfItem] = newValue 這種是無法檢測的。 -
只能劫持對象的屬性
只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷,如果屬性值也是對象那麼需要深度遍歷,顯然能劫持一個完整的對象是更好的選擇
-
需要 hack
Proxy 的優點
- Proxy 可以直接監聽數組的變化
- Proxy 返回的是一個新對象,我們可以只操作新的對象達到目的,而非屬性
- Proxy 支持的攔截操作一覽,一共 13 種,比如:has、apply、construct、deleteProperty…
- 兼容性問題
瞭解Proxy更多詳情:https://blog.csdn.net/m_review/article/details/90443242