在我的上一篇文章中,很清晰的講述瞭如何實現虛擬DOM,實現完了以後我就想到了在Vue當中,有template這個模板標籤,我們在真實開發中,不可能是像寫虛擬DOM那樣去描述一個頁面的UI結構,我們還是希望寫的是正常的HTML代碼,這樣纔能有可讀性和維護性。
// 我們不會以這種形式去寫HTML
let a = h('ul', { id: 'ol-list' }, ['123123',
h('li', { class: 'item1', style: "list-style: none" }, ['Item1']),
h('li', { class: 'item2', id: 'li2', style: "color: pink", onclick: 'printStr ()' }, ['Item2']),
h('li', { class: 'item3', onclick: 'alert(1234)' }, ['Item3'])
]).render()
所以在實現了虛擬DOM後,我就在思考如何將一段HTML轉換爲虛擬DOM,這樣既能方便開發,也能爲接下來的研究diff算法起到一定幫助,最後通過使用dom節點的屬性實現了這個過程。接下來就直接講述代碼了,這裏不再重複如何實現Virtual DOM,不知道如何實現的朋友可以查看我的上篇文章如何實現Virtual DOM。
整理下思路:
- 首先我們通過DOM API獲取到根節點,對它的標籤名、標籤屬性、子節點進行解析。
- 通過vnode.tagName屬性拿到標籤名,使用Object.entries函數解析標籤屬性,通過vnode.childNodes屬性拿到子節點並轉爲數組。
- 將上述三個參數傳入CreateEl類中,生成虛擬DOM,再調用render方法渲染到瀏覽器中去。
下面就是代碼部分:
首先定義好一段HTML模板代碼,這裏暫時還是以div標籤來代替
<div id="template">
模板結構
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
接下來是獲取根節點,獲取整顆DOM Tree的所有信息,最後返回一顆虛擬DOM Tree:
// 將dom樹節點劫持到對象中中進行解析
function nodeToObject(vnode) {
let parseProps = {}
// 定義一個parseProps接收節點屬性
for (let [key, val] of Object.entries(vnode.attributes)) {
parseProps[val.name] = val.value
}
let dealChildren = []
// 定義一個數組接收節點的所有子節點,對於元素則遞歸調用nodeToObject方法
let parsrChildren = Array.from(vnode.childNodes)
parsrChildren.forEach((ele) => {
if (ele instanceof Element) {
dealChildren.push(nodeToObject(ele))
} else {
dealChildren.push(ele.nodeValue)
}
})
let flag = {
tag: vnode.tagName.toLowerCase(),
props: parseProps,
children: dealChildren
}
// 返回一個虛擬Dom Tree
return h(flag.tag, flag.props, flag.children)
}
這個時候就大公告成了,我們能夠解析傳入的標籤中所有的信息,並將它渲染成真實DOM Tree並渲染到瀏覽器上:
let AST = nodeToObject(document.getElementById('template')).render();
document.body.appendChild(AST);
最終渲染出來的效果是這樣子的: