<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                *本文的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 ![](https://img.kancloud.cn/82/71/8271dd1eb3d9421ecfba30070d409a33_841x738.png) ## snabbdom的帶樣式插件、事件插件的使用 ![](https://img.kancloud.cn/d7/62/d762780cb30f2b2da34265de047118e4_912x503.png) # 源碼解析 ## snabbdom的核心 * h 函數創建vnode(js對象)用來描述真實dom * init函數,掛載模塊,創建patch()函數 * patch()函數 對比新舊vnode節點 * 更新真實dom 官方源碼地址:https://github.com/snabbdom/snabbdom (注意本文0.7.4版本),down下來后,目錄如下: ![](https://img.kancloud.cn/4d/28/4d28b7cda7336339fea60d813d02f5a3_418x609.png) 我會用care src目錄和examples目錄即可。 ## h( )函數 關于h( ) ,我們知道是用來創建vnode的(對象)。 其實我假設你沒看過vue源碼,那么你也應該眼熟h()函數,vue在初始化時會用到,當然,在你沒有了解源碼前,應該不會關注vue初始化為什么要傳一個這樣的參數: ![](https://img.kancloud.cn/a9/25/a92527781d72cfafee4fb03ab70c2237_455x178.png) > 在了解h函數前需要先知道重載,js是沒有重載的,ts通過傳入參數不同,代碼調整參數,模擬實現了重載。 我們來看看h.ts的關鍵代碼:![](https://img.kancloud.cn/f2/74/f27413af7b888118b0fa65d612ef4cd8_1484x801.png) 關鍵的都寫在注釋中,可以發現h()函數就是經過一系列重載參數判斷,然后對`children`屬性做處理,將可能不是`vnode`的項轉成`vnode`,然后調用vnode函數,返回vnode. ## vnode函數 在上面h函數中,我們調用了一個vnode函數 返回一個vnode,我們看看他是怎么實現的。 先把調用的參數放一邊好看 ![](https://img.kancloud.cn/ab/5d/ab5d7e81f2c58e966d603d38cb0dad82_557x344.png) 帶注釋的源碼如下: ![](https://img.kancloud.cn/38/fb/38fba853802717870e94227bae67f79b_997x607.png) ## init() 代碼結構如下: ![](https://img.kancloud.cn/4a/19/4a1965e165849c8d80134af74eefa52e_1067x883.png) 這里調用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過程只進行同級比較 ![](https://img.kancloud.cn/3e/f6/3ef65eae1aa434186067b247a141e527_1337x753.png) 首先會調用 `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 函數 ![](https://img.kancloud.cn/2a/6b/2a6b51df98046a6c42bf084a59c0533d_1299x930.png) 這里的邏輯也很清晰,首先會調用元素的 `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過程: ![](https://img.kancloud.cn/a4/83/a48373612d1fa227637d0be953a235b6_1077x658.png) ![](https://img.kancloud.cn/37/e4/37e495452ae6b5acb84c5ca3e346c5d1_1344x788.png) ### updateChildren函數 diff對比邏輯如下(這部分來源于網絡,迅雷前端): ![](https://img.kancloud.cn/a7/49/a749aa005e0ab28dc9a105d0efa3b350_1343x851.png) ![](https://img.kancloud.cn/ac/60/ac60d836dec88a12040d03137cd72e64_1369x882.png) ![](https://img.kancloud.cn/cc/81/cc8147b0c7ba5b7d7576ed7ac2ae18db_1341x909.png) 整個過程簡單來說,對兩個數組進行對比,找到相同的部分進行復用,并更新。整個邏輯可能看起來有點懵,可以結合下面這個例子理解下: 1. 假設舊節點順序為\[A, B, C, D\],新節點為\[B, A, C, D, E\] ![](https://img.kancloud.cn/5c/f0/5cf0984a8c0c10b87a807f03b9ee1846_1280x747.png) 2.第一輪比較:開始結束節點兩兩并不相等,于是看 newStartVnode 在舊節點中是否存在,最后找到了在第二個位置,調用 patchVnode 進行更新,將 oldCh\[1\] 至空,將 dom 插入到 oldStartVnode 前面,newStartIdx 向中間移動,狀態更新如下 ![](https://img.kancloud.cn/f3/8d/f38de27a15a7e577d80d9fbfaf89cce0_1280x770.png) 3. 第二輪比較:oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下 ![](https://img.kancloud.cn/fb/f9/fbf94b03e979611a3c9960b5108cef44_1280x779.png) 4. 第三輪比較:oldStartVnode 為空,oldStartIdx 向中間移動,進入下輪比較,狀態更新如下 ![](https://img.kancloud.cn/5a/b6/5ab66cc8a2c6f5d062dd0298411b2ed1_1216x786.png) 5. 第四輪比較:oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下 ![](https://img.kancloud.cn/91/a4/91a436b03a92fdd6996edc010b2dd053_1276x864.png) 6. oldStartVnode 和 newStartVnode 相等,直接 patchVnode,newStartIdx 和 oldStartIdx 向中間移動,狀態更新如下 ![](https://img.kancloud.cn/70/18/7018e8c23ea47b3405813201ceb2bfe5_1232x942.png) 7. oldStartIdx 已經大于 oldEndIdx,循環結束,由于是舊節點先結束循環而且還有沒處理的新節點,調用 addVnodes 處理剩下的新節點 ![](https://img.kancloud.cn/7a/2a/7a2a26135f03ad294919ae9f33cc9189_1320x864.png)(圖片引用自互聯網)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看