*本文的snabbdom庫是基于:"snabbdom": "^0.7.4"*
*本文的對應git倉庫地址分支:[git倉庫鏈接](https://github.com/fujiazhang/vue/tree/snabbdom-use) 建議down下來,因為給源碼挨個寫了中文注釋??*
*****
這篇文章virtual dom 庫 snabbdom的閱讀筆記,Virtual DOM 也越來越火,snabbdom 是其中一種實現,他號稱是最快的,哈哈哈哈,因為最近在看vue源碼,而vue 2.0版本的 Virtual DOM 部分也是基于snabbdom,而且關鍵的是他的代碼不多,很適合源碼的學習,而且還是用的typescript的,ts閱讀起來就挺舒服。
[TOC]
# snabbdom使用
## snabbdom基本api

## snabbdom的帶樣式插件、事件插件的使用

# 源碼解析
## snabbdom的核心
* h 函數創建vnode(js對象)用來描述真實dom
* init函數,掛載模塊,創建patch()函數
* patch()函數 對比新舊vnode節點
* 更新真實dom
官方源碼地址:https://github.com/snabbdom/snabbdom (注意本文0.7.4版本),down下來后,目錄如下:

我會用care src目錄和examples目錄即可。
## h( )函數
關于h( ) ,我們知道是用來創建vnode的(對象)。
其實我假設你沒看過vue源碼,那么你也應該眼熟h()函數,vue在初始化時會用到,當然,在你沒有了解源碼前,應該不會關注vue初始化為什么要傳一個這樣的參數:

> 在了解h函數前需要先知道重載,js是沒有重載的,ts通過傳入參數不同,代碼調整參數,模擬實現了重載。
我們來看看h.ts的關鍵代碼:
關鍵的都寫在注釋中,可以發現h()函數就是經過一系列重載參數判斷,然后對`children`屬性做處理,將可能不是`vnode`的項轉成`vnode`,然后調用vnode函數,返回vnode.
## vnode函數
在上面h函數中,我們調用了一個vnode函數 返回一個vnode,我們看看他是怎么實現的。
先把調用的參數放一邊好看

帶注釋的源碼如下:

## init()
代碼結構如下:

這里調用init函數,返回patch函數,使用的是高階函數,形成閉包,好處是patch數據可以訪問init傳入參數domapi,modules,這樣不用每次都去傳入,好處是非常明顯和巧妙的~ 。通過參數可以知道,這里有接受一個 `modules` 數組,另外有一個可選的參數 `domApi`,如果沒傳遞會使用瀏覽器中和 `dom` 相關的 api,這樣的設計也很有好處,它可以讓用戶自定義平臺相關的 api,這里會對 `module` 中的 `hook` 進行收集,保存到 `cbs` 中。然后定義了各種函數,這里可以先不管,接著就是返回一個 `patch` 函數了,這里也先不分析它的具體邏輯。這樣 `init` 就結束了。這里先按住不表,后面會展開講。
## patch
patch函數是init函數返回的,patch的作用就是對比新舊節點,更新真實dom,patch內部整體過程如下:
* ptach(oldVnode,newVnode)
* 打補丁,把新節點中華變得內容渲染到真實dom,最后返回新節點作為下一次patch的舊節點
* 對比新舊vnode是否相同節點(key 、sel對比)
* 如果不是相同 節點,刪除之前節點,重新渲染
* 如果是相同的,再判斷新的VNode是否由text, 如果有并且和oldVnode的text不同,直接更新文本內容
* 如果新的Vnode有childern, 判斷子節點是否變化,判斷子節點的過程即使用diff算法
* diff過程只進行同級比較

首先會調用 `module` 的 `pre hook`,你可能會有疑惑,為什么沒有調用來自各個元素的 `pre hook`,這是因為元素上不支持 `pre hook`,也有一些 `hook` 不支持在 `module` 中,具體可以查看[這里的文檔](https://github.com/snabbdom/snabbdom#overview)。然后會判斷傳入的第一個參數是否為 `vnode` 類型,如果不是,會調用 `emptyNodeAt` 然后將其轉換成一個 `vnode`,`emptyNodeAt` 的具體實現也很簡單,注意這里只是保留了 `class` 和 `style`,這個和 `toVnode` 的實現有些區別,因為這里并不需要保存很多信息,比如 `prop` `attribute` 等。接著調用 `sameVnode` 來判斷是否為相同的 `vnode` 節點,具體實現也很簡單,這里只是判斷了 `key` 和 `sel` 是否相同。如果相同,調用 `patchVnode`,如果不相同,會調用 `createElm` 來創建一個新的 `dom` 節點,然后如果存在父節點,便將其插入到 dom 上,然后移除舊的 `dom` 節點來完成更新。最后調用元素上的 `insert hook` 和 `module` 上的 `post hook`。 這里的重點是 `patchVnode` 和 `createElm` 函數,我們先看 `createElm` 函數,看看是如何來創建 `dom` 節點的。
### createElm 函數

這里的邏輯也很清晰,首先會調用元素的 `init hook`,接著這里會存在三種情況:
* 如果當前元素是注釋節點,會調用 `createComment` 來創建一個注釋節點,然后掛載到 `vnode.elm`
* 如果不存在選擇器,只是單純的文本,調用 `createTextNode` 來創建文本,然后掛載到 `vnode.elm`
* 如果存在選擇器,會對這個選擇器做解析,得到 `tag`、`id` 和 `class`,然后調用 `createElement` 或 `createElementNS` 來生成節點,并掛載到 `vnode.elm`。接著調用 `module` 上的 `create hook`,如果存在 `children`,遍歷所有子節點并遞歸調用 `createElm` 創建 `dom`,通過 `appendChild` 掛載到當前的 `elm` 上,不存在 `children` 但存在 `text`,便使用 `createTextNode` 來創建文本。最后調用調用元素上的 `create hook` 和保存存在 `insert hook` 的 `vnode`,因為 `insert hook` 需要等 `dom` 真正掛載到 `document` 上才會調用,這里用個數組來保存可以避免真正需要調用時需要對 `vnode` 樹做遍歷。
接著我們來看看 `snabbdom` 是如何做 `vnode` 的 `diff` 的,這部分是 `Virtual DOM` 的核心。
### patchVnode 函數
這個函數做的事情是對傳入的兩個`vnode`做`diff`,如果存在更新,將其反饋到`dom`上。
patchVnode過程:


### updateChildren函數
diff對比邏輯如下(這部分來源于網絡,迅雷前端):



整個過程簡單來說,對兩個數組進行對比,找到相同的部分進行復用,并更新。整個邏輯可能看起來有點懵,可以結合下面這個例子理解下:
1. 假設舊節點順序為\[A, B, C, D\],新節點為\[B, A, C, D, E\]

2.第一輪比較:開始結束節點兩兩并不相等,于是看 newStartVnode 在舊節點中是否存在,最后找到了在第二個位置,調用 patchVnode 進行更新,將 oldCh\[1\] 至空,將 dom 插入到 oldStartVnode 前面,newStartIdx 向中間移動,狀態更新如下

3. 第二輪比較:oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下

4. 第三輪比較:oldStartVnode 為空,oldStartIdx 向中間移動,進入下輪比較,狀態更新如下

5. 第四輪比較:oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下

6. oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下

7. oldStartIdx 已經大于 oldEndIdx,循環結束,由于是舊節點先結束循環而且還有沒處理的新節點,調用 addVnodes 處理剩下的新節點
(圖片引用自互聯網)
- 前言
- 工作中的一些記錄
- 破解快手直播間的webSocket的連接
- 快手「反」反爬蟲的研究記錄
- HTML AND CSS
- 遇到的一些還行的css筆試題
- css常見面試題
- JavaScript 深度剖析
- ES6到ESNext新特性
- 關于http與緩存
- 關于頁面性能
- 關于瀏覽器的重排(reflow、layout)與重繪
- 手寫函數節流
- 手寫promise
- 手寫函數防抖
- 手寫圖片懶加載
- 手寫jsonp
- 手寫深拷貝
- 手寫new
- 數據結構和算法
- 前言
- 時間復雜度
- 棧
- 隊列
- 集合
- 字典
- 鏈表
- 樹
- 圖
- 堆
- 排序
- 搜索
- Webpack
- Webpack原理與實踐
- Vue
- Vuejs的Virtual Dom的源碼實現
- minVue
- Vuex實現原理
- 一道關于diff算法的面試題
- Vue2源碼筆記:源碼目錄設計
- vue-router源碼分析(v4.x)
- React及周邊
- 深入理解redux(一步步實現一個 redux)
- React常見面試題匯總
- Taro、小程序等
- TypeScript
- CI/CD
- docker踩坑筆記
- jenkins
- 最后