proxy實現vue3數據雙向綁定原理
一、proxy對比Object.defineProperty的優點
proxy的優點:
- Proxy 可以直接監聽對象而非屬性;
- Proxy 可以直接監聽數組的變化;
- Proxy 有多達 13 種攔截方法,不限於 apply、ownKeys、deleteProperty、has 等等是Object.defineProperty 不具備的;
- Proxy 返回的是一個新對象,我們可以隻操作新的對象達到目的,而 Object.defineProperty 隻能遍歷對象屬性直接修改;
- Proxy 作為新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利;
Object.defineProperty 的優勢:
兼容性好:支持 IE9,而 Proxy 的存在瀏覽器兼容性問題,而且無法用 polyfill 磨平,因此 Vue 的作者才聲明需要等到下個大版本( 3.0 )才能用 Proxy 重寫。
二、、proxy監聽對象的簡單實現
1.代理對象簡單實現
```javascript let data = {};// 定義一個空對象 let proxy = new Proxy(data, {});// 創建一個 Proxy , 將 data 作為目標對象 // 修改Proxy 代理對象的name屬性 proxy.name = 'shelley'; console.log(proxy); console.log(data) // { name: 'shelley' } // { name: 'shelley' } ```
2.補充知識 Reflect
Reflect
對象與Proxy對象一樣,也是 ES6 為瞭操作對象而提供的新 API
我們需要在 handler.set()
中 return 一個 Reflect.set(…arguments
) 來進行賦值給目標對象。
- Reflect.set方法設置target對象的name屬性等於value。如果name屬性設置瞭賦值函數,則賦值函數的this綁定receiver。
- Reflect.get方法查找並返回target對象的name屬性,如果沒有該屬性,則返回undefined。
3.proxy方法
handler.set()方法 屬性設置操作的捕捉器。
```javascript let data = { name: 'shelley', age: '27' }; let p = new Proxy(data, { set(target, prop, value) { // target = 目標對象 // prop = 設置的屬性 // value = 修改後的值 console.log(target, prop, value); // { name: 'shelley', age: '27' } age 18 return Reflect.set(...arguments); } }) p.age = 18; console.log(data); // { name: 'shelley', age: 18 } ```
– handler.get() 屬性讀取操作的捕捉器。
```javascript let data = { name: 'shelley', age: 22 }; let p = new Proxy(data, { get(target, prop){ console.log(target, prop);//{ name: 'shelley', age: 22 } age return Reflect.get(...arguments); } }) console.log(p.age);//22 ```
Object.defineProperty監聽對象的簡單實現
```javascript var o = {};// 創建一個新對象 var bValue = 39;// 在對象中添加一個設置瞭存取描述符屬性的示例 Object.defineProperty(o, 'bValue', { // 這代碼不會設置 o 的屬性,隻有訪問的時候才會 get() { return bValue; }, set(newValue) { console.log('set==>', newValue); bValue = newValue; } }); console.log(o) // {} // 進入訪問器代理的bValue屬性的get方法,返回,並設置o對象裡的bValue的值為38 console.log(o.bValue); // 38 // 進入訪問器代理的bValue屬性的set方法,設置bValue的新值, // 再進入get返回,並設置o對象裡的bValue的值為40 o.bValue = 40; console.log(o.bValue) // 40 ```
小結:
- es6 proxy代理器對比
es5 Object.defineProperty
,功能更加強大,提供瞭方法超多,甚至可以代理方法 - 為什麼vue3.0才使用es6的proxy,未在2.0就使用;因為es6在部分瀏覽器中並未兼容,如ie的低版本,所以在**大部分主流瀏覽器都兼容**的情況下,才使用
三、手寫vue3.0雙向綁定-es6 Proxy
1、什麼是Proxy
- Proxy取其英文意思即“代理”。所謂代理,是你要取得某樣東西或對其進行某些操作的中間媒介,而不是直接作用在這個對象上。
- Proxy可以理解成在目標對象前架設一層攔截層,外界訪問該對象都必須先通過這層攔截,因此提供一種機制可以對外界的訪問進行攔截或過濾。
2、vue.js中使用雙向綁定
```javascript <div id="app"> <h2>{{msg}}</h2> <input type="text" v-model="msg"/> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: 'shelley' }, }) </script> ```
四、Proxy對比Object.defineProperty
Vue2.0中的雙向綁定,使用Object.defineProperty()進行雙向綁定
缺點:
- 無法對數組進行監聽,采用的是對數組的方法進行重寫(push, pop,shift,unshift等等)。對此進行雙向綁定和數據監聽的操作
- 效率差,這主要是因為對多層數據進行一次性的遞歸操作,如果數據很多或者是很深層次,這樣性能非常的差
- 因為局限性,無法對新加/刪除的數據進行監聽,所以使用在vue2.0中使用$set進行手動添加
– Object.definePorperty()遞歸遍歷所有對象的所有屬性,當數據層級較深時,會造成性能影響。
– Object.definePorperty()隻能作用在對象上,不能作用在數組上。
– Object.definePorperty()隻能監聽定義時的屬性,不能監聽新增屬性。
– 由於Object.definePorperty()不能作用於數組,vue2.0選擇通過重寫數組方法原型的方式對數組數據進行監聽,但是仍然無法監聽數組索引的變化和長度的變更
Vue3.0中雙向綁定,使用Proxy和Reflect進行雙向綁定
優點:
- Proxy可以對數組和對象進行攔截和監聽
缺點:
- Proxy會出發多次set/get響應
解決辦法:
- ①使用類似於
debounce
的操作,對其進行優化,使其值響應一次 - ②(vue3.0中的解決方式),判斷key是否是target的自身屬性,以及value是否和target[key]相等,可以避免多餘的set/get操作
Proxy隻能代理一層,無法深度監聽
- ①使用深度遞歸,對每一層進行監聽。巧妙的使用的Reflect.get()會返回對象內層結構的特性(下一層),判斷下一層是否還是對象,並且使用深度遞歸操作。但是在性能上又很大的影響
- ②使用weakMap,使用兩個weakMap來保存原始數據和可響應數據。訪問數據時會從保存的數據中查找,如果沒有再對其進行Proxy操作。
到此這篇關於proxy實現vue3數據雙向綁定原理的文章就介紹到這瞭,更多相關proxy實現vue3數據雙向綁定 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一篇文章搞懂JavaScript中的代理和代理的使用
- Vue3使用Proxy實現數據監聽的原因分析
- vue3響應式Proxy與Reflect的理解及基本使用實例詳解
- vue3結構賦值失去響應式引發的問題思考
- Vue API中setup ref reactive函數使用教程