当react和vue中引入了虚拟dom后,极大的节省了计算资源也提高了页面的渲染速度。虚拟dom其实就是用来描述dom节点的一个js对象,通过对象的嵌套来描述完整的dom树,这样的话dom的每次修改都是在js对象的属性变化,这样一来查找js对象的属性要比遍历dom树节省资源。
比如我们页面需要显示的是
<div id = 'test'>
<div style="color:blue">节点一</div>
<div class="item">节点二</div>
<div onclick="alert(333)">节点三</div>
</div>
那么它的虚拟dom就如同下面的js对象一样,(当然真正的虚拟dom不会像我写的这样简单,对象中的属性可能比我写的这个还要多)
{
"flag": "HTML",
"tag": "div",
"data": { "id": "test" },
"children": [
{
"flag": "HTML",
"tag": "p",
"data": { "style": { "color": "blue" }},
"children": {
"flag": "TEXT",
"tag": null,
"data": null,
"children": "节点1",
"childreFlag": "EMPTY"
},
"childrenFlag": "SINGLE",
"el": null
}, {
"flag": "HTML",
"tag": "p",
"data": { "class": "item" },
"children": {
"flag": "TEXT",
"tag": null,
"data": null,
"children": "节点2",
"childreFlag": "EMPTY"
},
"childrenFlag": "SINGLE",
"el": null
}, {
"flag": "HTML",
"tag": "p",
"data": { "@click":()=>{alert(333)} },
"children": {
"flag": "TEXT",
"tag": null,
"data": null,
"children": "节点3",
"childreFlag": "EMPTY"
},
"childrenFlag": "SINGLE",
"el": null
}
],
"childrenFlag": "MULTIPLE",
"el": null
}
那么虚拟dom如何渲染到页面上呢?
vue中会通过diff算法对修改的节点进行比较修改虚拟dom对应的属性,然后使用render函数将修改后的虚拟dom转换成真是dom然后挂载到父节点上。
react是通过虚拟 dom 和 setState 更改 data 生成新的虚拟 dom 以及 diff 算法来计算和生成需要替换的 dom 做到局部更新的
如何解析虚拟dom呢(这是自己写的一个render)
function render(vnode,container){
mount(vnode,container)
}
function mount(vnode,container){
let {flag} = vnode;
if(flag === vnodeType.HTML){
mountElement(vnode,container)
}else if(flag === vnodeType.TEXT){
mountText(vnode,container)
}
}
function mountElement(vnode,container){
let dom = document.createElement(vnode.tag);
vnode.el = dom;
let {data,children,childrenFlag} = vnode;
if(data){
for(let key in data){
patchData(dom,key,null,data[key])
}
}
if(childrenFlag !== childType.EMPTY){
if(childrenFlag === childType.SINGLE){
mount(children,dom)
}else if(childrenFlag === childType.MULTIPLE){
for(let i=0; i<children.length; i++){
mount(children[i],dom)
}
}
}
container.appendChild(dom)
}
function mountText(vnode,container){
let dom = document.createTextNode(vnode.children)
vnode.el = dom container.appendChild(dom)
}
function patchData(el, key,prv,next){
switch(key){
case 'style':
for(let k in next){
el.style[k] = next[k]
}
break;
case 'class':
el.className = next
break;
default:
if(key[0] === '@'){
if(next){
el.addEventListener(key.slice(1),next)
}
}else{
el.setAttribute(key,next)
}
break;
}
}