虚拟DOM是什么?
虚拟DOM简单来说是一个JavaScript对象,相当于给js和dom之间做了一个缓存,在真正执行dom前,先利用虚拟dom做好各项工作,最后一次性操作dom
<div id="app">
<p class="text">hello world!!!</p>
</div>
以上HTML转换为虚拟DOM
cosnt node={
tag: 'div',
props: {
id: 'app'
},
chidren: [
{
tag: 'p',
props: {
className: 'text'
},
chidren: [
'hello world!!!'
]
}
]
}
React中的虚拟DOM
const VNode={
key:null,
props:{
children:[ //子元素
{type:'span',...},
{type:'span',...}
],
className: "red"
onclick:()=>{}
},
ref:null,
type:"div", //标签名 or 组件名
...
}
React中创建虚拟DOM
createElement('div'{
className:'red',
onClick:()=>{},[
createElement('span',{},'span1'),
createElement('span',{},'span2'),
])
以上简写
<!--通过babel转换为以上形式-->
<div className="red" onClick="{()=>{}}">
<span>span1</span>
<span>span1</span>
</div>
Vue中的虚拟DOM
const VNode={
tag: "div",
data: {
class: "red",
on: {
click: ()=>{}
}
},
children:[ //子元素
{tag:'span',...},
{tag:'span',...}
],
...
}
Vue中创建虚拟DOM
h('div',{
className:'red',
on:{
click: ()=>{}
},
},[h('span',{},'span1'),h('span',{},'span2')]
以上简写
<!--通过vue-loader转换为以上形式-->
<div class="red" @click="fn">
<span>span1</span>
<span>span2</span>
</div>
虚拟DOM的优点
- 将多次操作合并为一个,如添加100个节点只需操作一次
- 可以节省多余的操作。如添加1000个节点,其中只有100个是新的,虚拟DOM只会添加新增的节点。
虚拟DOM的缺点
需要额外的创建函数,但是可以简化,如createElement和h,可以通过JSX来简化成XML写法(vue-loader配合vue模板写法)。也因此依赖于打包工具
DOM diff是什么?
是一个函数
changed=patch(oldVNode,newVNode)
changed是要运行的dom操作,像这样
[
{type: 'INSERT', vNode: ... },
{type: 'TEXT', vNode: ... },
{type: 'PROPS', propsPatch: [...]}
]
举例,
<div class="red">
<span>hello</span>
<span>world</span>
</div>
若修改第一个span的内容为world,删除第二个span,会产生两棵树,进行tree diff,对两棵树逐层对比:
- div没变,不用更新
- 第一个span标签不变,更新children内容
- 第二个span不见了,删除对应的dom
最后将所有操作记录下来,一次性处理
如果节点是组件就看Component diff
如果是标签就看Element diff
对于组件的diff:
- 先看组件类型
- 类型不同直接替换,删除旧组件
- 类型相同只更新属性,
- 然后深入组件做tree diff
对于元素的diff:
- 是原生标签,看标签名
- 标签名不同直接替换,相同则只看属性
- 进入标签进行tree diff
diff存在问题
比如有vue中有一个data数组,array: [1,2,3]
<template>
<div id="app">
<Child v-for="item,i in array" @delete="remove(i)"></Child>
</div>
</template>
methods:{
remove(i){
this.array.splice(i,1)
}
}
三个元素删除了中间一个,[1,2,3]变成[1,3],看上去仅仅是删除了2,但是按照diff的逻辑,进行新旧dom树的对比:
- 首先对比1和1,没问题
- 对比2和3,变化了,将2改为3
- 对比3和undefined,3不见了,删除3 因此导致明明删除的是2,但实际上删除的却是3,仅仅是把2变成了3
为了避免这一问题,需要在使用v-for时绑定一个key
参考自:
- 饥人谷《虚拟DOM和dom diff》
- 虚拟DOM到底是什么