题目描述
给出如下虚拟dom的数据结构,如何实现简单的虚拟dom,渲染到目标dom树
// 样例数据
let demoNode = ({
tagName: 'ul',
props: {
'class': 'list'},
children: [
({
tagName: 'li', children: ['douyin']}),
({
tagName: 'li', children: ['toutiao']})
]
});
构建一个render
函数,将demoNode
对象渲染为以下dom
<ul class="list">
<li>douyin</li>
<li>toutiao</li>
</ul>
实现
/*
* @Params:
* tagName(string)(requered)
* props(object)(optional)
* children(array)(optional)
* */
function Element({
tagName, props, children}){
if(!(this instanceof Element)){
return new Element({
tagName, props, children})
}
this.tagName = tagName;
this.props = props || {
};
this.children = children || [];
}
通过Element
我们可以任意地构建虚拟DOM
树了。但是有个问题,虚拟的终归是虚拟的,我们得将其呈现到页面中,不然,没卵用。。
怎么呈现呢?
从上面得知,这是一颗树嘛,那我们就通过遍历,逐个节点地创建真实DOM
节点:
createElement;
createTextNode;
因为这是一颗树嘛,对于树形结构无外乎两种遍历:
- 深度优先遍历(DFS)
- 广度优先遍历(BFS)
针对实际情况,我们得采用DFS
,为什么呢?
因为我们得将子节点append
到父节点中
好了,那我们采用DFS
,就来实现一个render
函数吧,如下:
Element.prototype.render = function(){
var el = document.createElement(this.tagName),
props = this.props,
propName,
propValue;
for(propName in props){
propValue = props[propName];
el.setAttribute(propName, propValue);
}
this.children.forEach(function(child){
var childEl = null;
if(child instanceof Element){
childEl = child.render();
}else{
childEl = document.createTextNode(child);
}
el.appendChild(childEl);
});
return el;
};
此时,我们就可以轻松地将虚拟DOM呈现到指定真实DOM中啦。假设,我们将上诉ul虚拟DOM呈现到页面body中,如下:
var elem = Element({
tagName: 'ul',
props: {
'class': 'list'},
children: [
Element({
tagName: 'li', children: ['item1']}),
Element({
tagName: 'li', children: ['item2']})
]
});
document.querySelector('body').appendChild(elem.render());