虚拟DOM是什么?
虚拟DOM的本质是JS对象,是一个较为轻量级的描述DOM的对象。
为什么说虚拟DOM是一个轻量级的描述DOM
真实的DOM
<ul id='list'>
<li class='item'>item1</li>
<li class='item'>item2</li>
</ul>
虚拟DOM:只模拟了DOM节点的一部分内容,可以尝试从控制台打印一下真实的DOM节点,就会发现实际DOM节点中的属性是很复杂的
{
tag:'ul',
attrs:{
id:'list'
},
children:[
{
tag:'li',
attrs:{
className:'item',
children:['item']
}
tag:'li',
attrs:{
className:'item',
children:['item2']
}
}
]
}
为什么会存在虚拟DOM,虚拟DOM的优势和劣势
- DOM的操作非常昂贵,频繁的操作DOM,会引起页面的重绘和重排,影响页面的性能。
- 虚拟DOM中,将DOM的变化放在JS层来做,patch过程中,尽可能的一次性将差异更新到DOM中。
- 无需手动操作DOM,可以提高开发效率。
- 跨平台:可以基于虚拟DOM根据需求输出Web DOM、Android控件或者IOS控件。
虚拟DOM的核心类库—— snabbdom
核心函数有h函数和patch函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
</head>
<body>
<div id='container'></div>
<button id='btn-change'>修改</button>
<script type="text/javascript">
var snabbdom = window.snabbdom
var h = snabbdom.h
var patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
])
var container = document.getElementById('container')
var vnode = h('ul#list',{},[
h('li.item',{},'item1'),
h('li.item',{},'item2')
])
var newVnode = h('ul#list',{},[
h('li.item',{},'item1'),
h('li.item',{},'item22'),
h('li.item',{},'item3')
])
patch(container,vnode)
var btn = document.getElementById('btn-change')
btn.addEventListner('click',function(){
patch(vnode,newVnode)
})
</script>
</body>
</html>
diff算法
是一个linux的命令,vdom中应用diff算法是为了找出需要更新的节点
虚拟DOM转换为真实DOM的过程和diff的过程
patch(container,vnode) 形式
function createElement (vnode){
var tag = vnode.tag
var attrs = vnode.attrs || {}
var children = vnode.children || []
if(!tag){
return nll
}
//创建元素
var elem = document.createElement(tag)
var attrName
for(attrName in attrs){
if(attrs.hasOwnProperty(attrName)){
elem.setAttribute(attrName,attrs[attrName])
}
}
//子元素
children.forEach(function(childVnode){
//递归
elem.appendChild(createElement(childVnode))
})
//返回真是的DOM元素
return elem
}
patch(vnode,newVnode)
function updateChildren(vnode,newVnode){
var children = vnode.children || []
var newChildren = newVnode.children || []
children.forEach(function(child,index){
var newChild = newChildren[index]
if(newChild == null){
return
}
if(child.tag === newChild.tag){
updateChildren(child,newChild)
}else{
repalce(child,newChild)
}
})
}
vdom为什么使用diff算法
- DOM操作是昂贵的,尽量减少DOM操作
- 找出DOM必须更新的节点来更新,其他的不更新
- 找出的过程,就是diff算法的实现