用過vue的人都知道 vue2.x實現雙向數據綁定的原理是利用了 Object.defineProperty() 這個方法重新定義了對象獲取屬性值(get)和設置屬性值(set)的操作來實現的。
在MDN上對該方法的說明是:Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
它接收三個參數,要操作的對象,要定義或修改的對象屬性名,屬性描述符。重點就是最后的屬性描述符。
屬性描述符是一個對象,主要有兩種形式:數據描述符和存取描述符。
這兩種對象只能選擇一種使用,不能混合兩種描述符的屬性同時使用。上面說的get和s et就是屬於存取描述符對象的屬性。
然后我們可以通過在存取描述符中的get和set方法內寫入自定義的邏輯來實現對象獲取屬性和設置屬性時的行為。
用代碼演示vue2.x的原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<input type="text" id="input">
<span id="text"></span>
</div>
</body>
</html>
<script>
var obj = {};
Object.defineProperty(obj, 'prop', {
get: function () {
return val;
},
set: function (newVal) {
val = newVal;
document.getElementById('text').innerHTML = val;
}
});
document.addEventListener('keyup', function (e) {
obj.prop = e.target.value;
});
</script>
瀏覽器展示

Vue3.x是用ES6的語法 Proxy對象來實現的,這個玩意兒也可以實現數據的劫持
用代碼演示vue3.x的原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<input type="text" id="input">
<span id="text"></span>
</div>
</body>
</html>
<script>
var obj = {};
var obj1 = new Proxy(obj, {
// target就是第一個參數obj, receive就是返回的obj(返回的proxy對象)
get: function (target, key, receive) {
// 返回該屬性值
return target[key];
},
set: function (target, key, newVal, receive) {
// 執行賦值操作
target[key] = newVal;
document.getElementById('text').innerHTML = target[key];
}
})
document.addEventListener('keyup', function (e) {
obj1[0] = e.target.value;
});
</script>
瀏覽器展示為

相比於vue2.x,使用proxy的優勢如下
- 1 defineProperty只能監聽某個屬性,不能對全對象監聽
- 2 可以省去for in、閉包等內容來提升效率(直接綁定整個對象即可)
- 3 可以監聽數組,不用再去單獨的對數組做特異性操作
- vue3.x可以檢測到數組內部數據的變化
