Vitural Dom & diff演算法 (一) 虛擬碼實現

發表於2015-12-18

前言:

讀React 原始碼大概是最幸福的事情,因為在社群裡有數之不盡的高手都會給出自己很獨到的見解,即使有不懂的地方也會能找到前人努力挖掘的痕跡。我反問自己,然後即使在這樣優越的環境下,還不去讀原始碼,是不是就太懶了。 # 我會亂說? #

約定

這一篇都通過虛擬碼實現,保證首先從總體上走通流程,後面的篇章都是基於這樣一個流程去實現。

開始之前

這裡必須明確一個概念,所謂的 自定義標籤 都是由很多原生標籤諸如<div>去實現的。

因此自定義標籤就可以想象成

流程

  1. 建立虛擬DOM
  2. 真實DOM 連線 虛擬DOM
  3. 檢視更新
  4. 計算 [ 新虛擬DOM ] 和 [ 舊虛擬DOM ] 的差異 ( diff )
  5. 根據計算的 差異, 更新真實DOM ( patch )

這裡牽涉到兩個詞語 diff 和 patch,稍後再解釋,這裡簡單理解為 [計算差異]和[應用差異]。

虛擬碼實現

注:雖然這是這個系列的第一篇,但這已經是第二遍寫了。原因是第一遍想完整模擬的時候發現,自己對演算法的瞭解太粗淺,深搜和最短字元演算法都不懂,最近和死月大大請教,所以這裡偏向思路多一點。

1. 這裡我們期望的真實DOM結構是這樣的,下面我們一步步實現

2. 建立虛擬DOM

3. 虛擬DOM不能夠影響真實DOM,這裡我們需要建立連線

最終目的得到這樣的字串,可以供真實DOM使用

簡單實現 ( 這裡需要記錄一下每個DOM的id )

這裡做的事情,就是遍歷虛擬DOM,然後拼合虛擬DOM後,以字串形式輸出。

4. 現在我們已經建立了連線了,現在需要模擬一下檢視更新,於是我們新生成一個虛擬DOM。

注意:由於React是基於setState的方法去觸發檢視更新的,但這裡要描述的重點是diff,因此我們簡單帶過。

5. 我們終於進入主題了!我們激動的去比較一下它們的差異

先別激動!

為了更好的描述這個問題,我們這裡改變一下上面獲取的兩個虛擬DOM。

我們打算把結構換成這樣的,注意註釋的部分,就是兩個DOM的不同之處。

6. 比較差異

6. 扁平化

為了方便比較,我們把虛擬DOM,變成Map型別的資料

8. diff

這裡開始我們就正式開始比較了

這裡注意到我們生成新的Map是通過 generateNewMap 方法的。 generateNewMap 實際上就是去遞迴呼叫diff,完成所有子類的差異比較,最終返回到差異陣列。

7. 差異型別

這裡簡單用整型數字作為紀錄

8. 應用差異patch

我們已經得到了所有的差異diffQueue,是時候應用這些改動了。

拿插入作例子,我們知道了掛載的物件以及插入的位置,那麼我就可以用原生的insertBefore去執行。

那麼通過這些計算得到的source子元素和在目標掛載元素target中的位置index,我們就能夠應用這些變化。

下面簡單程式碼再描述一下

function patch(diffQueue){

10. 大體流程再次迴歸
  1. diff遞迴找出不同,存入差異陣列。每一個差異元素包含自身位置,父元件位置,差異型別
  2. 根據差異型別和差異資訊,對舊的虛擬DOM作處理
  3. 所有處理結束後,一次性操作真實DOM完成處理
9. 總結

呼…雖然是第二遍寫,還是不能很流暢表達,感覺抽象出來的粒度還不夠,不過機智的我已經預料到了這一切。

所以第二篇會開始用演算法真刀真槍去模擬整個庫的形式去解讀。每學習到一種解決方案還是很開心的,啦啦啦。

10. 特別推薦
  1. http://purplebamboo.github.io/

    博主貌似叫竹隱,文章質量太高了,是那種看完久久不能平復,就算是半夜都想爬起來擼程式碼,這篇文章就是照擼了一遍,還不夠爽,第二遍自己用虛擬碼擼的。

  2. https://github.com/livoras/blog/issues

    抽象能力非常強,加以通俗的語言解釋高深的演算法,受啟發好大

  3. https://github.com/Matt-Esch/virtual-dom

    權威的virtual-dom解讀,看issues的討論感覺腦洞真的好大…

  4. http://facebook.github.io/react/docs/reconciliation.html
    http://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf

    官方的解讀和完整的演算法解釋( 論文 ) …心目中前端的最終形態

相關文章