實現一個簡單的虛擬DOM

codeXiu發表於2019-06-02

學習過框架尤其是VUE、React的肯定都知道Virtual DOM這個概念。因為我也很想知道並且瞭解它是一個什麼東西,所以我打算把自己學習的分享個大家。如有錯誤,請大家指明。

首先,說一下什麼是虛擬DOM。

  • 虛擬DOM就是用JS物件來表示或者是模擬一個真實DOM的結構。

其實就是一個JS的物件。我們先來簡單的實現一個虛擬DOM。我們使用過React的都知道createElement這一個函式、Vue都知道render或者是“h”,這樣一個函式。我們先來建立一個createElement函式。

/**
 * [createElement 用來建立DOM節點]
 * @param  {[type]} type     [元素型別(名稱)]
 * @param  {[type]} props    [描述資訊]
 * @param  {[type]} children [子節點]
 * @return {[type]}          [description]
 */
function createElement(type, props, children) {
    // 返回一個Element物件。
    return new Element(type, props, children);
}
複製程式碼
<div class="vdom">561651</div>
    type: div
    props: class="vdom"
    children: 561651
複製程式碼

元素物件(Element),用來表示一個元素。

class Element {
    constructor(type, props, children) {
    	this.type = type;
    	this.props = props;
    	this.children = children;
    }
}
複製程式碼
let vDom = createElement("ul", {class: "dawd"}, [
    	createElement("li", {class: "dawd"}, ["1"]),
    	createElement("li", {class: "dawd"}, ["2"]),
    	createElement("li", {class: "dawd"}, ["3"])
    ]);
複製程式碼

實現一個簡單的虛擬DOM
已經基本描述出了DOM的樹形結構。下面我們來根據虛擬DOM建立真實的DOM。在這之前我們先建立元素節點(單個元素)。

/**
 * [createNode 建立單個元素]
 * @param  {[type]} node [元素節點]
 * @return {[type]}      [真實的DOM元素]
 */
function createNode(node){
    // 根據型別建立元素
    let el = document.createElement(node.type);
    for (key in node.props) {
        // 遍歷屬性 
    	if(key === "value"){
    	    // 只有input還有textarea需要value屬性
    		if(node.type.toUpperCase() === "INPUT" || node.type.toUpperCase() === "TEXTAREA"){
    	 		el.value = node.props[key];
    		}
    	}else {
    	    // 設定屬性
    		el.setAttribute(key, node.props[key]);
    	}
    }
    return el;
}
複製程式碼

根據單個元素組成DOM樹

function createDom(node) {
    let root = createNode(node);
    if(node.children && node.children.length > 0){
        // 遍歷子元素
    	node.children.forEach( function(element) {
    		if(element instanceof Element){
    		    // 節點
    			root.appendChild( createDom(element) );
    		}else {
    		    // 文字
    			root.appendChild( document.createTextNode(element) );
    		}
    	});
    }
    return root;
}
複製程式碼

根據虛擬DOM生成的真實DOM

實現一個簡單的虛擬DOM
現在只是生成了真實的DOM但是還沒有真正的掛載到DOM樹上,沒有顯示。

let dom = createDom(vDom);
document.getElementsByTagName("body")[0].appendChild(dom);
複製程式碼

實現一個簡單的虛擬DOM

再試一下input元素

let vDom = createElement("ul", {class: "dawd"}, [
			createElement("li", {class: "dawd"}, [
				createElement("input", {type: "radio",value: "1651"},[]),
				createElement("input", {type: "text",value: "1651"},[])
			])
		]);
複製程式碼

真實DOM

實現一個簡單的虛擬DOM
顯示效果
實現一個簡單的虛擬DOM

我們再來一個複雜一點的來驗證是否正確。

createElement("div", {class: "div"}, [
	createElement("ul", {class: "ul"}, [
		createElement("li", {class: "li"},[createElement("input", {type: "radio",value: "1651"},["單選"])]),
		createElement("li", {class: "li"},[createElement("input", {type: "text",value: "1651"},[])]),]),
		createElement("div", {class: "div"}, [
			createElement("p", {class: "p"},[
		            createElement("span", {class: "span"},["我是span"])]),
			    createElement("a", {class: "a",href: "https://juejin.im/editor/drafts/5cf3c75de51d45572c05fff3"},[
				    createElement("span", {class: "span"},["我是超連結裡面的span"])]),
				    createElement("img", {class: "img",src: "http://g.hiphotos.baidu.com/image/h%3D300/sign=b5e4c905865494ee982209191df4e0e1/c2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg",alt: "虛擬DOM圖片",title: "虛擬的DOM"},[])
	]),
]);
複製程式碼

實現一個簡單的虛擬DOM

  • 總結
    • 虛擬DOM對我們的專案效能很有幫助,我們重點的是要了解他的思想和實現,不是一味的使用或者是照抄。
    • 說到虛擬DOM就離不開DOM diff演算法,我打算把他們分了兩篇文章來寫,首先一篇文章會太長。其次,我覺得這兩個概念雖然慼慼相關但是我覺得還是兩個東西。diff演算法我覺得是對DOM更新等操作的優化,減少無用的更新,這樣會帶來更少的消耗更多的效能。

我會盡快寫一篇有關DOM diff演算法 的文章,希望你們能耐心等候。

相關文章