虚拟DOM是什么?
一个能代表DOM树的对象,通常含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。
虚拟DOM的优点:
DOM每发生一次改变,浏览器都会进行一次完整的渲染(重排与重绘),有了虚拟DOM之后,DOM发生改变时先修改对应虚拟DOM,虚拟DOM的改变并不会直接渲染,而是等所有改变完成之后进行渲染,避免不必要的操作从而提高性能。
- 减少不必要的DOM操作(下边这两个例子要背下来)。
- 虚拟DOM可以将多次操作合并为一次操作,减少DOM操作的次数。(比如你添加1000个节点却是一个接一个操作的)
- 虚拟DOM借助DOM diff算法可以把多余的操作省掉,减少DOM操作的范围(比如你添加1000个节点,其实只有10个是新增的)。
- 跨平台渲染:
由于虚拟dom是个对象,所以可以跨平台并支持服务端渲染。
虚拟DOM长什么样子?
// Vue
const vDode = {
tag: "div", //标签名 or 组件名
data: {
class: "red", // 标签上的属性
on: {
click: () => {} // 事件
}
},
children: [
{tag: "span", ...},
{tag: "span", ...}
],
...
}
如何创建一个虚拟DOM
// ReactcreateElement
createElement('div', {className: 'red' , onClick:() => {}},[
createElement('span', {}, 'span1'),
createElement('span', {}, 'span2')
])
// Vue(只能在render函数里得到h)
h('div', {
class: 'red',
on: {
click: () => {}
},
}, [h('span', {}, 'span1'), h('span', {}, 'span2')])
改进(用JSX简化创建虚拟DOM)
// React 通过babel 转为 createElement 形式
<div className="red" onClick = {fn}>
<span>span1</span>
<span>span2</span>
</div>
// Vue 通过vue-loader转换为 h 形式
<div class="red" @click="fn">
<span>span1<span>
<span>span2<span>
</div>
虚拟DOM的缺点
当首次渲染大量DOM或者全部DOM,因为多了一层虚拟DOM的计算,反而比直接渲染慢
需要额外的创建函数,比如createElement(React)或h(Vue),但可以通过JSX来简化成XML写法。
DOM diff
什么是DOM diff?
DOM diff就是比较两个虚拟DOM之间有什么差异的算法
DOM diff就是一个函数,我们称之为patch
patches = patch(oldVNode, newVNode)
patches 就是我们要运行的DOM操作。
DOM diff的大概逻辑
将新旧两棵树逐层对比,找出哪些节点需要更新。
如果节点是组件就看Component diff,如果检点是标签就看Element diff
Component diff
如果节点是组件,就看组件类型,类型不同直接替换(删除旧的),类型相同就只更新属性,然后深入组件做Tree diff(递归)
Element diff
如果节点是原生标签,则看标签名,标签名不同直接替换,相同则只更新属性。然后进入标签后代做Tree diff(递归)。
DOM diff的优点
对比新旧节点,查找出差异并只修改差异部分,极大提高性能。
DOM diff的问题(key)
当虚拟DOM没有唯一标识符(key)时,默认以索引作为标识,那么两个相同的节点由于索引不同依然会进行修改删除等操作。
网友评论