跳到主要内容

谈谈虚拟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
text
React.createElement('div',{className:'red',onClick= ()=>{}},[
React.createElement('span',{},'span1')
])
  • 由于写虚拟DOM太复杂和繁琐了,React就提供了JSX语法来简化虚拟DOM的写法
  • jsx是React.createElement的语法糖
  • 通过babel可以把jsx代码转换为React.createElement

虚拟DOM结构大致样子

  • 一个能代表DOM树的对象,通常含有标签名,标签上的属性,事件监听和子元素,以及其他属性
text
{
key:null,
props:{
children:[
{ type:'span',xxx}
],
className:'red',
onClick:()=>{}
},
ref:null,
type:'div',
.......
}