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
算法 + 生命週期鉤子】這樣的核心模型。前端
在上一篇博文《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
就能夠了。框架
至此咱們完成了模板的編譯,也獲得了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)
的方法經過一樣的遍歷方法來收集變化並批量更新視圖。
本篇只是部分原理的學習筆記,並不表明框架真實源碼的實現邏輯。