淺談 Vue3 雙向綁定的概念與實作
前言
接下來這一篇將會針對 Vue3 來做一下淺談,其中也會包含實作的部分。
Vue
在前一篇 「淺談 Vue2 雙向綁定的概念與實作」有分享了基本 Vue2 的雙向綁定主要來自 Object.defineProperty
,但是在 Vue3 之後 Vue 做出了一個非常重要的改變,也就是 Vue3 改使用 ES6 Proxy
用於取代原本的 Object.defineProperty
,最常聽到的就是效能有非常大的提升。
使用 Proxy 製作除了效能有非常大的提升之外,最主要是有兩個缺點:
- 無法深層監聽資料,因此在 Vue 中是採用迴圈方向去往深層監聽,因此效能上就會比較差。
- 陣列長度若發生變化會失效。
這邊也可以看到 Vue2 的官方文件「检测变化的注意事项」中有這一段話:
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。
上面這一段是什麼意思呢?首先先讓來講講一個基本觀念,相信學習 Vue 的人都會聽到一句話「請盡可能預先定義好你的資料,否則會出現問題」,因此如果你的 data
中沒有這個屬性,後來再新增時是無法雙向綁定的:
1 | <div id="app"> |
1 | var vm = new Vue({ |
那麼由於無法雙向綁定,因此若綁定到畫面時是會出現 ReferenceError: b is not defined
的錯誤,如果看不出來的話,也可以試著將程式碼改成以下:
1 | <div id="app"> |
1 | var vm = new Vue({ |
我們都知道如果使用了 Object.defineProperty
的話會具備有 Setter
與 Getter
,但這邊後來新增的 b
屬性並沒有:
(若對於 Object.defineProperty
不熟建議閱讀這一篇文章)
因此官方就有提供了一個方法可以解決,也就是 Vue.$set
語法。
那麼接下來聊聊陣列的長度部分,在官方文件中就有舉例到兩點 Vue 無法監聽的陣列狀況:
当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
通常來講,我們會使用到陣列的時候大多都是為了跑迴圈才使用陣列,而這邊我也查了一下相關文獻,剛好就有一篇 文章 有人提到了這件事情,簡單來講就是,針對陣列去做 Getter 與 Setter 是非常吃效能的,所以通常都必須額外拉出來做效能處理,詳情可以直接看上面文章即可。
這邊你也可以看到實際範例,如果修改了陣列長度或是確實是會失去雙向綁定的:
1 | <div id="app"> |
1 | var vm = new Vue({ |
因此你只能使用 Vue 有處理過的語法去操作陣列,否則是會失去雙向綁定:
1 | push(); |
ES6 Proxy
接下來聊聊 Proxy,簡單來講他有非常多的操作(狹持、操作)方法(高達 13 種)而且可以代理一整個物件並且返回一個新的物件,以下是它的基本用法
1 | let p = new Proxy(target, handler); |
剛剛也有講到它會返回一個新的物件因此不會修改到原始物件,而寫法也與原本的 Object.defineProperty
雷同:
1 | var obj = { |
Proxy 的參數主要有兩個:
target
(目標)- 可以是任何東西,舉凡物件、陣列或是函式都可以
handler
(操作)- 主要是一個物件,裡面會有許多
target
的操作行為
- 主要是一個物件,裡面會有許多
那麼由於 handler
中的方法太多,所以建議直接考 MDN 文件。
那麼接下來聊聊實作雙向綁定的部分,絕大部分程式碼會直接使用 淺談 Vue2 雙向綁定的概念與實作 這一篇,因此不會重新再說明一次程式碼邏輯,這邊就直接貼上 Object.defineProperty
調整成 new Proxy
的結果:
1 | <div id="app"> |
1 | var model = { |
以上就是簡單的淺談與將上一篇 Vue2 改成 Proxy 的方式而已。