虚拟DOM是什么
虚拟DOM其实是一个js对象,他能代表DOM树,通常含有标签名、事件监听和子元素等属性。
- Vue中的虚拟DOM
const vNode = {
tag: "div", // 标签/组件名
data: {
class: "child", // 标签上的属性
on: {
click: fn // 事件
}
},
children: [ // 子元素
{ tag: "div", ... },
{ tag: "span", ... }
],
...
}
- React中的虚拟DOM
const vNode = {
key: null,
props: {
children: [ // 子元素
{ type: 'div', ... },
{ type: 'span', ... }
],
className: "child" // 标签上的属性
onClick: fn // 事件
},
ref: null,
type: "div", // 标签/组件名
...
}
上面是Vue和React中的虚拟DOM的样子。那么,我们如何才能创建虚拟DOM呢?
在Vue中,我们可以使用h函数
const App = new Vue({
render(h=>{
h('div', {
class: 'child',
on: {
click: fn
},
}, [h('div',{},'div1'), h('span', {}, 'span1'])
})
})
在React中,我们可以使用React.createElement
const App = React.createElement(
'div',{className:'red',onClick:()=> {}},[
createElement('span', {}, 'span1'),
createElement('span', {}, 'span2')
]
)
虚拟DOM的优点
- 能减少不必要的DOM操作
-
次数方面 例如我们需要在页面中创建1000个节点,那么真实DOM需要一个一个的去创建,而虚拟DOM可以借助DOM diff将其合并为一次操作
-
范围方面 还是创建1000个节点,如果这1000个节点中只有20个是新增的,那么通过虚拟DOM和DOM diff ,就只需要增加那多出来的20个节点就可以了。
- 跨平台 虚拟DOM的本质是一个js对象,所以虚拟DOM不仅可以用于web,还能用于ios,安卓,小程序等地方。
虚拟DOM的缺点
上面我们提到了,要创建虚拟DOM需要用到h函数或者React.createElement函数,而且,为了方便书写,我们会用jsx或者.vue文件来书写虚拟节点。
这样的话,使用虚拟节点就需要额外的创建函数,打包时需要额外的babel/loader。
DOM diff 是什么
上面我们多次提到了 DOM diff 那么,DOM diff到底是什么呢?
DOM diff 是虚拟DOM的对比算法。
DOM diff会将虚拟DOM树进行层层对比,首先对比标签名/组件名,然后是属性,然后是内容,然后计算出已经变化的地方,最后返回一个新的标签/组件。
DOM diff 的问题
在DOM diff 中,有一个关键的问题,那就是计算机的比对和我们人类的比对不太一样。
比如下面有两个数组
const array1 = [1,2,3]
const array2 = [1,3]
对于DOM diff来说,在比对 array[0] 时,发现都是1,没有变化,
在对比 array[1] 时,发现2变成了3,所以dom diff会将 原先的2变为3,
而在对比 array[2] 时,发现 一个是3,一个是undefined,所以会将3直接删除。
这就会导致 DOM diff 的效率变慢,解决办法是添加一个key,这样的话当DOM diff 对比两个数组时就会发现,数组只是减少了一项,而不再需要像上面那样去繁琐的对比。