全面理解虛擬DOM(1)

最近一兩年前端最火的技術莫過於 reactjs,angularJS,vuejs,即使你沒用過也可能聽過,像ReactJS由業界頂尖的互聯網公司facebook提出,其自己有不少先進的設計思路,好比頁面UI組件化、虛擬DOM等。本文將帶你解開虛擬DOM的神祕面紗,不只要理解其原理,並且要實現一個基本可用的虛擬DOM。javascript

1.爲何須要虛擬DOM

       DOM是很慢的,其元素很是龐大,頁面的性能問題鮮有由JS引發的,大部分都是由DOM操做引發的。若是對前端工做進行抽象的話,主要就是維護狀態和更新視圖;而更新視圖和維護狀態都須要DOM操做。其實近年來,前端的框架主要發展方向就是解放DOM操做的複雜性。前端

       在jQuery出現之前,咱們直接操做DOM結構,這種方法複雜度高,兼容性也較差;有了jQuery強大的選擇器以及高度封裝的API,咱們能夠更方便的操做DOM,jQuery幫咱們處理兼容性問題,同時也使DOM操做變得簡單;可是聰明的程序員不可能知足於此,各類MVVM框架應運而生,有angularJS、avalon、vue.js等,MVVM使用數據雙向綁定,使得咱們徹底不須要操做DOM了,更新了狀態視圖會自動更新,更新了視圖數據狀態也會自動更新,能夠說MMVM使得前端的開發效率大幅提高,可是其大量的事件綁定使得其在複雜場景下的執行性能堪憂;有沒有一種兼顧開發效率和執行效率的方案呢?ReactJS就是一種不錯的方案,雖然其將JS代碼和HTML代碼混合在一塊兒的設計有很多爭議,可是其引入的Virtual DOM(虛擬DOM)倒是獲得你們的一致認同的。vue

2.理解虛擬DOM

虛擬的DOM的核心思想是:對複雜的文檔DOM結構,提供一種方便的工具,進行最小化地DOM操做。這句話,也許過於抽象,卻基本概況了虛擬DOM的設計思想java

(1) 提供一種方便的工具,使得開發效率獲得保證
(2) 保證最小化的DOM操做,使得執行效率獲得保證

(1).用JS表示DOM結構

DOM很慢,而javascript很快,用javascript對象能夠很容易地表示DOM節點。DOM節點包括標籤、屬性和子節點,經過VElement表示以下。react

//虛擬dom,參數分別爲標籤名、屬性對象、子DOM列表
var VElement = function(tagName, props, children) {
    //保證只能經過以下方式調用:new VElement
    if (!(this instanceof VElement)) {
        return new VElement(tagName, props, children);
    }

    //能夠經過只傳遞tagName和children參數
    if (util.isArray(props)) {
        children = props;
        props = {};
    }

    //設置虛擬dom的相關屬性
    this.tagName = tagName;
    this.props = props || {};
    this.children = children || [];
    this.key = props ? props.key : void 666;
    var count = 0;
    util.each(this.children, function(child, i) {
        if (child instanceof VElement) {
            count += child.count;
        } else {
            children[i] = '' + child;
        }
        count++;
    });
    this.count = count;
}

經過VElement,咱們能夠很簡單地用javascript表示DOM結構。好比程序員

var vdom = velement('div', { 'id': 'container' }, [
    velement('h1', { style: 'color:red' }, ['simple virtual dom']),
    velement('p', ['hello world']),
    velement('ul', [velement('li', ['item #1']), velement('li', ['item #2'])]),
]);

上面的javascript代碼能夠表示以下DOM結構:app

<div id="container">
    <h1 style="color:red">simple virtual dom</h1>
    <p>hello world</p>
    <ul>
        <li>item #1</li>
        <li>item #2</li>
    </ul>   
</div>

一樣咱們能夠很方便地根據虛擬DOM樹構建出真實的DOM樹。具體思路:根據虛擬DOM節點的屬性和子節點遞歸地構建出真實的DOM樹。見以下代碼:框架

VElement.prototype.render = function() {
    //建立標籤
    var el = document.createElement(this.tagName);
    //設置標籤的屬性
    var props = this.props;
    for (var propName in props) {
        var propValue = props[propName]
        util.setAttr(el, propName, propValue);
    }

    //依次建立子節點的標籤
    util.each(this.children, function(child) {
        //若是子節點仍然爲velement,則遞歸的建立子節點,不然直接建立文本類型節點
        var childEl = (child instanceof VElement) ? child.render() : document.createTextNode(child);
        el.appendChild(childEl);
    });

    return el;
}

對一個虛擬的DOM對象VElement,調用其原型的render方法,就能夠產生一顆真實的DOM樹。dom

vdom.render();

既然咱們能夠用JS對象表示DOM結構,那麼當數據狀態發生變化而須要改變DOM結構時,咱們先經過JS對象表示的虛擬DOM計算出實際DOM須要作的最小變更,而後再操做實際DOM,從而避免了粗放式的DOM操做帶來的性能問題。工具

相關文章
相關標籤/搜索