虚拟DOM和DOM iff

132 阅读2分钟

虚拟DOM

虚拟DOM是什么

虚拟dom的本质一个能代表dom树的JS对象,包含tag、props、children三个属性

虚拟DOM的例子

React虚拟DOM

const vNode = {
	key:null,
    props: {
    	children: [
        	{type: 'span', ...},
            {type: 'span', ...}
        ],
        className: "red",
        onClick:()=>{}
    },
    ref:null
    type:'div',
    ...
 }
 
 //创建虚拟DOM
 
 createElement('div',
 	{className:'red',
 	onClick:()=>{}},
    [
    	createElement('span',{},'span1'),
        createElement('span',{},'span2')
    ])
 
 使用JSX写法
 
 <div className="red" onClick="{()=>{}}">
 	<span>span1</span>
    <span>span2</span>
 </div>

可通过webpack babel插件转为createElement的形式

Vue虚拟dom

const vNode = {
   tag:'div',
   data:{
   	class:"red",
   	on:{
   		click: ()=>{}
   	}
   },
   children: [
       {type: 'span', ...},
   	{type: 'span', ...}
   ],
   
   ...
}

//创建虚拟DOM(只能在render函数里得到h)
h('div',{
   class:'red',
   on:{click:()=>{}
  },
  [
  	h('span',{},'span1'),
   h('span',{},'span2')
  ])
  
// Vue Template
<div class="red" @click="fn">
	<span>span1</span>
   <span>span2</span>
</div>


通过vue-loader 转为h的形式

虚拟DOM优点

  • 减少DOM操作

    虚拟DOM可以将多次操作合并为一次操作,比如添加1000个节点,却是一个接一个操作

  • 跨平台

    虚拟DOM不仅可以变成DOM,还可以变成小程序、ios应用、安卓应用、因为虚拟DOM本质上只是一个JS对象

DOM缺点

需要额外的创建函数,如createELement或h,但是可以通过JSX来简化成XML写法,会依赖打包工具。

DOM diff

DOM diff 是一个函数,我们称之为patch,patches = patch(oldVNode,newVNode),patch就是要运行的DOM操作。

例子:

<div :class="x">
	<span v-if="y">{{string1}}</span>
	<span>{{string2}}</span>
</div>

把虚拟DOM想象成树形

  1. 当数据发生变化时,x从red变成了green

DOM diff发现

div标签类型不变,只需要更新div对应的DOM属性,、 子元素没变,不更新

  1. 当数据发生变化时,y从true变成false DOM diff发现

div没变,不更新

子元素1标签没变,但是children变了(hello->world),更新DOM内容

子元素2不见了,删除对应DOM

存在一些小小的问题

DOM diff在同层级对比中有bug。造成页面渲染错误。同一层级的一组节点可以通过唯一的id进行区分, 所以可以给节点设定唯一的key。从而消除bug。key只能是number和string类型,一定不要用index作为key值。

DOM diff大概逻辑

Tree diff

将新旧两颗树逐层对比,找出哪些节点需要更新

如果节点是组件就看Component diff

如果节点是标签就看Element diff

Component diff

如果节点是组件, 就先看组件类型

类型不同直接替换(删除旧的)

类型相同则只更新属性

然后深入组件做Tree diff

Element diff

如果节点是原生标签,则看标签名

标签名不同直接替换,相同则只更新属性

然后进入标签后代做Tree diff(递归)