javascript基礎修煉(10)——VirtualDOM和基本DFS

1. Virtual-DOM是什麼

Virtual-DOM,即虛擬DOM樹。瀏覽器在解析文件時,會將html文檔轉換爲document對象,在瀏覽器環境中運行的腳本文件均可以獲取到它,經過操做document對象暴露的接口能夠直接操做頁面上的DOM節點。可是DOM讀寫是很是耗性能的,很容易觸發沒必要要的重繪和重排,爲了更好地處理DOM操做,Virtual-DOM技術就誕生了。Virtual-DOM就是在javascript中模擬真實DOM的結構,經過數據追蹤和狀態對比來減小對於真實DOM的操做,以此來提升程序的效率的一種技術。javascript

Virtual-DOM技術是前端高性能的基石,它是真實document對象的抽象,經過對比新舊Virtual-DOM的區別,找出發生變化的DOM節點,再利用算法獲得相對更合理的DOM節點修改方案,最終再將方案應用在document對象上來改變頁面的展現內容。html

主流前端SPA框架都離不開【Virtual-DOM模型 + DOM-Diff算法 + 生命週期鉤子】這樣的核心模型。前端

2. Virtual-DOM的基本結構

在上一篇博文《javascript基礎修煉(9)——MVVM中雙向數據綁定的基本原理》中,咱們經過document.getElementById()從真實DOM中得到了帶有自定義屬性的待解析結構,這裏是有一些問題的,實際的過程是先解析模板字符串獲得虛擬DOM樹,最後生成真實的DOM樹。java

實際上咱們在使用SPA框架時所編寫的html模板,並無被直接當作DOM片斷加載到頁面上使用,而是將文件當作字符串讀入到程序中,而後經過解析來生成Virtual-DOM樹,接着經過SPA框架的渲染函數來生成必要的片斷後才生成真實的DOM節點。例如咱們要生成下文示例的HTML片斷(爲了方便演示,示例中只涉及了類名和文本節點):算法

<body class="main">
   <div class="sideBar">
       <ul class="sideBarContainer">
           <li class="sideBarItem">sidebar-1</li>
           <li class="sideBarItem">sidebar-2</li>
           <li class="sideBarItem">sidebar-3</li>
       </ul>
   </div>
   <div class="mainContent">
        <div class="header">header-zone</div>
        <div class="coreContent">core-content</div>
        <div class="footer">footer-zone</div>
   </div>
   <div class="rightSide">暫未開發</div>
</body>

咱們須要構建出一個簡易模型來表達上面的結構:瀏覽器

virtualDom = {
    name:"body",
    props:{
        className:"main"
    },
    children:[{
        name:"div",
        props:{...},
        children:[...]
      },{
        name:"div",
        props:{...},
        children:[...]
      },{
        name:"div",
        props:{...},
        children:[...]
      }]
}

創建一個生成虛擬節點的輔助函數:數據結構

//構建DOM節點的輔助函數
function h(name, props, children) {
    return {
        name:name,
        props:props,
        children:children
    }
}
//手動生成virtual-DOM
var tree = h('body',{className:'main'},[
       h('div',{className:'sideBar'},[
          h('ul',{className:'sideBarContainer'},[
               h('li',{className:'sideBarItem'},['sidebar-1']),
               h('li',{className:'sideBarItem'},['sidebar-2']),
               h('li',{className:'sideBarItem'},['sidebar-3']),
            ])
        ]),
       h('div',{className:'mainContent'},[
           h('div',{className:'header'},['header-zone']),
           h('div',{className:'coreContent'},['core-content']),
           h('div',{className:'footer'},['footer-zone']),
        ]),
       h('div',{className:'rightSide'},['暫未開發'])
    ]);

經過上面的方法獲得的tree對象就涵蓋了模板片斷中的結構和關鍵信息。實際開發中並不須要像上面同樣手動來填寫DOM結構,能夠將模板字符串掛載到離線DOM節點上,而後在遞歸解析的同時來構建Virtual-DOM就能夠了。框架

3. 使用DFS從Virtual-DOM生成DOM

至此咱們完成了模板的編譯,也獲得了Virtual-DOM對象,但它彷佛並無什麼用處,畢竟咱們已經完成了對模板的解析,渲染出頁面沒什麼問題,其實Virtual-DOM對於首屏來講並無什麼特別重要的意義,它的價值在模型和視圖發生變化時纔會體現。上一篇博文的末尾咱們已經提到了更新視圖時的效率問題,當數據模型發生變化後,咱們須要一個方法來收集全部須要修改的DOM,併爲之提供高效的修改方式(你總不能一有變化就把整個網頁從新渲染,或者讓數據模型各自去修改各自綁定的DOM吧)。那麼爲了可以收集全部DOM節點的變化,咱們就須要遍歷全部節點。dom

對數據結構和算法有必定了解的讀者很容易想到,遍歷解析一個Virtual-DOM實際上就是對其進行先序深度優先遍歷(Pre-Order Depth-First-Search),本節中,咱們先預熱一下,使用這種方式來複現一下DOM結構。數據結構和算法

function dfswalking(tree) {
    var _childrenLength;
    //執行動做
    if (typeof tree.children[0] === 'string') {
        console.log(`<${tree.name} class="${tree.props.className}">${tree.children[0]}</${tree.name}>`);
    } else {
        console.log(`<${tree.name} class="${tree.props.className}">  -->`);
        for(var i = 0, _childrenLength = tree.children.length; i < _childrenLength; i++){
            dfswalking(tree.children[i]);
        }
    }
}

本例中僅打印出字符串的方式來展現,能夠在控制檯看到輸出結果:

下一篇博文中將分析如何經過domDiff(oldTree, newTree)的方法經過一樣的遍歷方法來收集變化並批量更新視圖。

4. 聲明

本篇只是部分原理的學習筆記,並不表明框架真實源碼的實現邏輯。

相關文章
相關標籤/搜索