五分鐘看懂Vue3-資料繫結

realmofwind發表於2019-10-10

Vue3在偉大祖國70週歲時終於和大家見面了。?

五分鐘看懂Vue3-資料繫結

在學習Vue3的資料繫結前,先預習一下相關知識:

1、Proxy,我之前寫過一篇Proxy介紹,可以參考下。

2、WeakMap

然後我們就能愉快的玩耍了。

Vue3中關於資料繫結這塊的程式碼主要在這裡

五分鐘看懂Vue3-資料繫結

我們主要關注reactive.ts和baseHandlers.ts這兩個檔案裡的內容。

1.Proxy已知的兩個問題

  • Proxy本身不支援物件內部的深度偵測,需要自己實現

  • Proxy本身支援陣列變化偵測,但會有多次觸發的風險

2.Vue3是如何解決

2-1

解決第一個問題,對於初學者,我們可以採用暴力遞迴的辦法,一開始就把物件內部的所有物件都用Proxy代理一遍。

五分鐘看懂Vue3-資料繫結

這樣操作的結果就是,proxy變成下圖所示的結果:

五分鐘看懂Vue3-資料繫結

可以看到,物件內部的物件和陣列都已經被代理了。當obj是一個非常大且複雜的物件時,這樣做效能肯定不好,我們來看看Vue是怎麼做的。

Vue中,一開始我們只是把最外層的物件做Proxy操作,而內層的物件,只有在需要訪問的時候,才會惰性地建立Proxy代理。為此,我們需要對handler函式做一下改造。

五分鐘看懂Vue3-資料繫結

完成上述操作後,Vue3還做了一件事,就是把動態建立的Proxy都存在一個WeakMap中,這樣我們就不用每次執行get操作的時候都會去動態建立了,畢竟這是很耗費記憶體的。在Vue3原始碼中我們可以有兩個WeakMap,就是幹這事的。為此,我們還需要改造一下createReactiveObject方法,傳入兩個WeakMap來做快取。另外,toRaw這個WeakMap幹嘛用的,留給大家思考一下?。

五分鐘看懂Vue3-資料繫結

2-2

下面我們來解決第二個問題,假設我們有一個陣列arr=[1,2],當我們執行arr.push(3)時,arr有兩個屬性發生了變化,一個是新增了一個key=2的索引屬性,對應value就是我們剛新增的3,這時可以把陣列理解成一個物件;此外arrlength屬性也發生了變化,增加了1。所以,如果用Proxy劫持了陣列的set方法時,此時會觸發兩次set操作,一次對應的key是2,一次對應的key是length

五分鐘看懂Vue3-資料繫結

每次執行set操作新增或者修改某個屬性時,我們先利用Object.hasOwnProperty判斷這個屬性是否存在,如果不存在則直接trigger add ...;如果是已有屬性,則判斷當前valuetarget的上相同key對應的value,也就是程式碼中的oldValue是否相等,如果不相等,才會去執行trigger set ...。這裡需要細細理解一下,當觸發keylength的回撥時,由於此時的target,就是被代理的陣列物件,在上一次key2的回撥觸發時,執行了Reflect.set(target, key, value, receiver),此時它的陣列長度已經+1了,所以在keylength的回撥中,此時oldValuevalue是相等的,都是3。這樣,我們就避免了多次觸發render的操作,Vue3中的這段程式碼還是寫的很精妙的,??。

到此,Proxy原生的兩個大問題都被合理解決了,接下來就是一些常規的前置條件判斷,Vue3中只對陣列、物件、Map、WeakMap、Set、WeakSet這幾類資料做了資料繫結,對基本資料型別和一些JS內建物件如Date、Promise和Reg等是直接返回不會做Proxy處理的。

下面我們看一眼本文中用到的demo和Vue3原始碼的對比,可以看到,大體的結構是一樣的,當然demo中做了很多的簡化操作。

五分鐘看懂Vue3-資料繫結

3.demo小結

看到這裡,你是不是對Vue3中的資料繫結有了一個大概的瞭解。完整demo程式碼在這裡。寫的不完善的地方,還請各位大佬多多指教。


相關文章