自己實現vue雙向繫結從vue2到vue3
在Vue3中,最重要也更為人所知的就是ES6的Proxy。
Proxy不僅消除了Vue2中現有的限制(比如物件新屬性的增加、陣列元素的直接修改不會觸發響應式機制,這也是很多新手以為所謂的bug),而且有著更好的效能:
我們知道,在Vue2中對資料的偵聽劫持是在元件初始化時去遍歷遞迴一個物件,給其中的每一個屬性用Object.defineProperty設定它的getter和setter,然後再把處理好的這些物件掛到元件例項的this上面,所以這種方式的資料偵聽是在屬性層面的,這也是為什麼增加物件屬性無法被監聽的原因,同時這種初始化的操作對於CPU來說還是比較昂貴的一個操作。對於javascript而言,一個物件肯定越穩定越小效能就越好。
使用Proxy之後元件的初始化就不需要做這麼費時的操作了,因為Proxy就是真正意義給一個物件包上一層代理從而去完成資料偵聽劫持的操作,所以總的來說這個複雜度是直接少了一個數量級的。只要你對這個代理後的物件訪問或修改裡面的東西都能被這層代理所感知到,進而去動態地決定返回什麼東西給你,並且也不再需要把選項的這些東西再重複掛到元件例項的this上面,因為你訪問的時候是有這個資訊知道你訪問的東西是屬於props還是data還是其他的,vue只需要根據這個資訊去對應的資料結構裡面拿出來就可以了,單就這一點而言你就可以感覺到元件的記憶體佔用已經少了一半。
由於proxy是在物件層面上的代理,所以你在物件上新增屬性是可以被代理到的。
另外Proxy還可以代理陣列,所以就算你直接修改數組裡面的元素也是可以被代理到的。
以上摘自《了不起的vue3》,下面上程式碼,自己實現vue的雙向繫結,幫助理解其原理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue雙向繫結原理</title> </head> <body> <div></div> <input type="text"> <script> // vue2.0 利用 Object.defineProperty 實現雙向繫結 let box, input, data, obserData; box = document.querySelector('div'); input = document.querySelector('input'); data = 'default'; obserData = {}; Object.defineProperty(obserData, 'data', { get: () => { return data; }, set: (newData) => { // 設定data時,更新檢視並更新資料 input.value = newData; box.innerHTML = newData; data = newData; } }) input.addEventListener('keyup', function () { obserData.data = this.value; }) // 模擬生命週期 setTimeout(() => { obserData.data = '更新'; }, 1000) </script> </body> </html>
在vue3.0中
<script>
let box, input, data;
box = document.querySelector('div');
input = document.querySelector('input');
data = 'default';
// vue3.0利用es6的proxy實現雙向繫結
const obserData = new Proxy({}, {
get: (obj, prop) => {
return obj[prop];
},
set: (obj, prop, value) => {
// 設定data時,更新檢視並更新資料
if (prop === 'data') {
input.value = value;
box.innerHTML = value;
data = value;
}
}
})
input.addEventListener('keyup', function () {
obserData.data = this.value;
})
// 模擬生命週期
setTimeout(() => {
obserData.data = '更新';
}, 1000)
</script>
上面是為了大家方便理解proxy而延用了vue2的思想寫的,實際上,data本身就是物件形式,proxy是直接對此進行代理的,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue雙向繫結原理</title>
</head>
<body>
<div></div>
<input type="text">
<script>
let box, input, data;
box = document.querySelector('div');
input = document.querySelector('input');
data = { data: 'default' };
// vue3.0利用es6的proxy實現雙向繫結
const obserData = new Proxy(data, {
get: (obj, prop) => {
return obj[prop];
},
set: (obj, prop, value) => {
// 設定data時,更新檢視並更新資料
if (prop === 'data') {
input.value = value;
box.innerHTML = value;
obj[prop] = value;
}
}
})
input.addEventListener('keyup', function () {
obserData.data = this.value;
})
// 模擬生命週期
setTimeout(() => {
obserData.data = '更新';
}, 1000)
</script>
</body>
</html>