谈谈虚拟DOM
- 虚拟DOM最大的优势是diff算法【也叫做差异算法】
- 减少了JavaScript操作真实DOM的带来的性能消耗
- 跨平台优势
为什么虚拟DOM可以跨平台?
用JS对象模拟DOM树
- 虚拟DOM本质上是JavaScript对象,它可以在不同平台上使用。它能够在不同平台之间共享相同的代码
为什么虚拟DOM的性能好?
- 因为虚拟DOM通过diff算法,只需要更新新旧DOM节点的差异,而不用整体的全量更新,减少不必要的性能损耗
- DOM的操作会触发浏览器的重绘和回流
react和vue的虚拟dom的差异
react和vue的虚拟dom都是用js对象来模拟真实DOM,用虚拟DOM的diff来最小化更新真实DOM,可以减小不必要的性能损耗
dom的更新策略不同
-
vue会跟踪每一个组件的依赖关系,局部渲染组件树而不需要重新渲染整个组件树。
-
React每当状态发生变化时候,子组件都会重新渲染,可通过memo/PureComponent/shouldComponentUpdate来优化
监听数据变化的实现原理不同
-
Vue 使用数据劫持和观察者模式来监听数据变化,当数据发生变化时,Vue 会通知订阅者进行重新渲染。这种方式可以保证页面的性能。
-
React 默认是通过比较引用来判断数据变化的
- 当组件的状态或者 props 发生变化时,React 会比对新旧状态或者 props 的引用是否相同
虚拟DOM实现原理
用JS对象模拟DOM树
- 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
- 状态变更时,对比新旧DOM节点的差异
- 最后把差异更新到真正的dom中
虚拟DOM可以说详细点吗?
递归找出有变化的dom节点,然后对其进行更新操作
- react会先比较该节点类型,假如节点类型不一样,那么
react会直接删除该节点,然后直接创建新的节点插入到其中- 假如节点类型一样,那么会比较props属性是否有更新,假如有props不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下对比,直到没有子节点
- 两个新旧DOM树,只会对同一层级节点进行比较
- 当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较,或者当发现节点不一样,会认为发生改变,直接删掉该节点,然后创建新的节点插入到里面
- 假如节点类型一样,那么会比较props属性是否有更新,假如有props不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下对比,直到没有子节点
- 对于同一层级的一组子节点,通过唯一的key进行区分
DOM Diff大概逻辑
-
tree diff
- 将新旧DOM树逐层对比,找出哪些节点需要更新
- 如果节点是组件就看component diff
- 如果节点是标签就看element diff
-
component diff
- 如果节点是组件,就看组件类型
- 类型不同直接替换 (删除旧的)
- 类型相同,就对比属性,则只更新属性
- 对组件进行递归的比较
-
element diff
- 如果节点是原生标签,就看标签名
- 标签名不同直接替换,相同就更新属性
- 然后对标签的后代子元素进行递归
虚拟DOM的优劣势?
优势:
-
虚拟dom 相当于在js 和真实dom 中间加了一个缓存,利用dom,diff 算法避免了没有必要的dom 操作,从而提高性能
-
减少DOM的操作
- 虚拟DOM可以将多次操作合并为一次操作,比如添加100个节点,dom可以会操作100次,但是虚拟DOM只需要操作1次 【减少DOM操作的次数】
- 虚拟DOM借助diff算法可以把多余的操作省掉,比如添加100个节点,但是发现已经有节点再页面上了,就不需要进行全量的更新,diff可以比较新旧DOM差异,只更新差异值 【减少DOM操作的范围】
-
跨平台
- 虚拟DOM不仅可以变成DOM,还可以变成小程序,安卓应用等,因为虚拟DOM本质上就是一个JS对象
劣势:
- 需要额外的创建函数createElement,写虚拟DOM会比较繁琐,可以使用JSX语法来简化
虚拟DOM和fiber的关系
-
fiber和虚拟DOM都是js对象
-
fiber可以使虚拟DOM进行增量式的渲染,能够在渲染工作分割成多块,分散到多个帧上,合理分配CPU资源
-
fiber会找出需要更新的DOM,将树的遍历变成链表的遍历
- 方便更新的时候进行暂停、终止,可以随时停止遍历并且可以恢复
-
fiber会打断diff算法
- fiber会优先处理优先级高的任务
- fiber把更新过程碎片化,每执行一段更新过程,就把控制权交给react负责任务的协调,看看有没有优先级紧急高的任务,如果有就优先去处理,如果没有,就继续下一步的更新
如何创建虚拟DOM?
- 在react中,通过React.createElement()创建虚拟DOM
React.createElement('div',{className:'red',onClick= ()=>{}},[
React.createElement('span',{},'span1')
])
- 由于写虚拟DOM太复杂和繁琐了,React就提供了JSX语法来简化虚拟DOM的写法
- jsx是React.createElement的语法糖
- 通过babel可以把jsx代码转换为React.createElement
虚拟DOM结构大致样子
- 一个能代表DOM树的对象,通常含有标签名,标签上的属性,事件监听和子元素,以及其他属性
{
key:null,
props:{
children:[
{ type:'span',xxx}
],
className:'red',
onClick:()=>{}
},
ref:null,
type:'div',
.......
}