<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ***** ## 1 vdom意義 ### 1-1 傳統界面操作 >[info] (1)傳統web界面操作中需要使用js操作DOM, 隨著應用程序的復雜,dom操作復雜度提升。 為了有效的組織這種復雜操作,提出了mvc,mvvm等結果 ### 1-2 vdom的意義 >[info] (2)在mvvm中通過數據綁定可以實現視圖與數據的互動效果 不需要手動更新頁面。只需要在模板中聲明視圖組件和綁定數據。 雙向綁定引擎vm可以自動實現數據與視圖的同步更新 >[info] (3)mvvm只是簡化了數據與視圖的關系。為了簡化模板引擎渲染, 可以使用vdom。生成新的視圖替換舊的視圖。 >[info] (4)vdom的核心是維護數據與視圖的關系 ## 2 vdom思路 >[info] (1)原生dom包含屬性較多。 直接操作dom可能導致頁面重排,影響渲染性能 可以將dom解析為js對象,操作js對象則簡單 ~~~ ;模板 <ul id='list'> <li class='item'>Item 1</li> <li class='item'>Item 2</li> <li class='item'>Item 3</li> </ul> ;js對象 var element = { tagName: 'ul', // 節點標簽名 props: { // DOM的屬性,用一個對象存儲鍵值對 id: 'list' }, children: [ // 該節點的子節點 {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]}, ] } ~~~ >[info] (2) 這樣就可以將數據的變化直接修改js對象, 對比修改后js的對象,記錄下需要對頁面真正的dom操作。 然后將其應用到真正的dom樹,實現頁面的更新 視圖的結構是整個全新渲染,最后操作dom只是修改局部 >[info] (3) 核心步驟如下 ~~~ 1 將dom樹轉換為js對象結構,生成真正dom樹,插入到文檔中 2 數據發生變化后,重新生成虛擬dom樹,進行比較 3 將比較結果保存到dom樹上 ~~~ >[info] (4) 可以將js對象看做真實dom的緩存部分。 ## 3 vdom之element >[info] 使用js對象記錄dom節點,只需要記錄節點類型,屬性和子節點 ~~~ ;element.js function Element (tagName, props, children) { this.tagName = tagName this.props = props this.children = children } module.exports = function (tagName, props, children) { return new Element(tagName, props, children) } ~~~ >[info] element.js簡單使用 ~~~ var el = require('./element') var ul = el('ul', {id: 'list'}, [ el('li', {class: 'item'}, ['Item 1']), el('li', {class: 'item'}, ['Item 2']), el('li', {class: 'item'}, ['Item 3']) ]) ~~~ >[info] element的生成真正dom ~~~ Element.prototype.render = function () { var el = document.createElement(this.tagName) // 根據tagName構建 var props = this.props for (var propName in props) { // 設置節點的DOM屬性 var propValue = props[propName] el.setAttribute(propName, propValue) } var children = this.children || [] children.forEach(function (child) { var childEl = (child instanceof Element) ? child.render() // 如果子節點也是虛擬DOM,遞歸構建DOM節點 : document.createTextNode(child) // 如果字符串,只構建文本節點 el.appendChild(childEl) }) return el } ~~~ >[info] 添加渲染結果到文檔 ~~~ var ulRoot = ul.render() document.body.appendChild(ulRoot) ~~~ ## 4 vdom之diff >[info] 對比兩個樹的diff是vdom算法的核心之一, 只是對同層的元素進行對比 ![](https://box.kancloud.cn/2016-05-07_572d98881781d.png) >[info] 對新舊兩個樹遍歷,記錄差異。 ![](https://box.kancloud.cn/2016-05-07_572db1e3c23c9.png) >[info] 遍歷節點把節點對比信息存儲到對象 ~~~ // diff 函數,對比兩棵樹 function diff (oldTree, newTree) { var index = 0 // 當前節點的標志 var patches = {} // 用來記錄每個節點差異的對象 dfsWalk(oldTree, newTree, index, patches) return patches } // 對兩棵樹進行深度優先遍歷 function dfsWalk (oldNode, newNode, index, patches) { // 對比oldNode和newNode的不同,記錄下來 patches[index] = [...] diffChildren(oldNode.children, newNode.children, index, patches) } // 遍歷子節點 function diffChildren (oldChildren, newChildren, index, patches) { var leftNode = null var currentNodeIndex = index oldChildren.forEach(function (child, i) { var newChild = newChildren[i] currentNodeIndex = (leftNode && leftNode.count) // 計算節點的標識 ? currentNodeIndex + leftNode.count + 1 : currentNodeIndex + 1 dfsWalk(child, newChild, currentNodeIndex, patches) // 深度遍歷子節點 leftNode = child }) } ~~~ >[info] 上面的記錄信息 ~~~ patches[0] = [{difference}, {difference}, ...] // 用數組存儲新舊節點的不同 ~~~ 同理p是patches[1],ul是patches[3],類推。 >[info] 差異類型 對于節點的對比結果可能分為以下幾種類型 替換掉原來的節點, 移動刪除新增子節點 修改節點屬性 修改節點文本 ~~~ var REPLACE = 0 var REORDER = 1 var PROPS = 2 var TEXT = 3 ~~~ >[info] 差異類型舉例 ~~~ ;替換差異 patches[0] = [{ type: REPALCE, node: newNode // el('section', props, children) }] ;屬性修改 patches[0] = [{ type: REPALCE, node: newNode // el('section', props, children) }, { type: PROPS, props: { id: "container" } }] ;文本修改 patches[2] = [{ type: TEXT, content: "Virtual DOM2" }] ~~~ >[info] 子節點列表對比算法 ~~~ ;舊節點順序 a b c d e f g h i ;新節點順序 a b c h d f g h i j ~~~ >[info] 列表中節點的操作可以看做移動 插入 刪除三種 移動可以看出刪除和插入的合并。因此,可以簡化為插入和刪除操作。 抽象出來就是字符串的最小編輯距離問題。 簡單實現如下 ~~~ patches[0] = [{ type: REORDER, moves: [{remove or insert}, {remove or insert}, ...] }] ~~~ ## 5 vdom之patch >[info] 可以對js對象進行對比后結果patches對象中 轉換為對應的dom操作實現dom樹的更新 ~~~ function patch (node, patches) { var walker = {index: 0} dfsWalk(node, walker, patches) } function dfsWalk (node, walker, patches) { var currentPatches = patches[walker.index] // 從patches拿出當前節點的差異 var len = node.childNodes ? node.childNodes.length : 0 for (var i = 0; i < len; i++) { // 深度遍歷子節點 var child = node.childNodes[i] walker.index++ dfsWalk(child, walker, patches) } if (currentPatches) { applyPatches(node, currentPatches) // 對當前節點進行DOM操作 } } function applyPatches (node, currentPatches) { currentPatches.forEach(function (currentPatch) { switch (currentPatch.type) { case REPLACE: node.parentNode.replaceChild(currentPatch.node.render(), node) break case REORDER: reorderChildren(node, currentPatch.moves) break case PROPS: setProps(node, currentPatch.props) break case TEXT: node.textContent = currentPatch.content break default: throw new Error('Unknown patch type ' + currentPatch.type) } }) } ~~~ ## 6 總結 >[info] vdom算法主要包含以上三個函數element,diff,patch 使用思路如下 ~~~ // 1. 構建虛擬DOM var tree = el('div', {'id': 'container'}, [ el('h1', {style: 'color: blue'}, ['simple virtal dom']), el('p', ['Hello, virtual-dom']), el('ul', [el('li')]) ]) // 2. 通過虛擬DOM構建真正的DOM var root = tree.render() document.body.appendChild(root) // 3. 生成新的虛擬DOM var newTree = el('div', {'id': 'container'}, [ el('h1', {style: 'color: red'}, ['simple virtal dom']), el('p', ['Hello, virtual-dom']), el('ul', [el('li'), el('li')]) ]) // 4. 比較兩棵虛擬DOM樹的不同 var patches = diff(tree, newTree) // 5. 在真正的DOM元素上應用變更 patch(root, patches) ~~~ ## 7 參考 [vdom算法](https://segmentfault.com/a/1190000004029168) [vdom完整代碼](https://github.com/livoras/simple-virtual-dom)
                  <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>

                              哎呀哎呀视频在线观看