虚拟DOM

192 阅读2分钟

虚拟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的优势和劣势

  1. DOM的操作非常昂贵,频繁的操作DOM,会引起页面的重绘和重排,影响页面的性能。
  2. 虚拟DOM中,将DOM的变化放在JS层来做,patch过程中,尽可能的一次性将差异更新到DOM中。
  3. 无需手动操作DOM,可以提高开发效率。
  4. 跨平台:可以基于虚拟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算法

  1. DOM操作是昂贵的,尽量减少DOM操作
  2. 找出DOM必须更新的节点来更新,其他的不更新
  3. 找出的过程,就是diff算法的实现