React虚拟dom与diff算法

1,091 阅读4分钟

1662371967675.jpg

一、虚拟dom

虚拟dom(Virtul DOM)。通过模拟DOM中的真实节点中的对象属性与方法,生成属于React自己的dom,再通过底层的render方法将其渲染成真实的DOM节点。

虚拟DOM的产生

直接更新页面上真实的 dom 成本很高,耗费性能大,virtual dom的出现解决了这个问题。

在React中,我们使用JSX来描述虚拟DOM的结构,然后React会通过渲染(render)过程将这些虚拟节点转换成真实的DOM节点,并挂载到页面上。这个过程涉及到了React的生命周期方法。

如果操作dom或更新了dom,React底层通过diff算法来对比新旧virtual dom后,找出不同地方,然后只更新发生变化的部分。这样来更新 dom 就大大地节省了成本,并且提供良好的用户体验。

虚拟dom的渲染过程

js生成vNode -> js渲染成真实节点 -> js挂载到页面 -> diff比较

虚拟DOM的渲染过程通常包括以下步骤:

  • 使用JSX创建虚拟节点(vNode)。
  • React的渲染机制将这些虚拟节点转换成真实的DOM节点。
  • 这些真实的DOM节点随后被挂载到页面上。
  • diff算法用于比较新旧虚拟DOM的差异,并只更新实际发生变化的部分。

二、diff算法

diff算法是React高效更新虚拟DOM的关键,它通过比较两次渲染的虚拟DOM差异来最小化DOM操作。

react特点之一,节省性能。diff算法就是为了节省性能而设计的。

1、diff算法的基本流程

首次渲染时,React会构建并缓存虚拟DOM的快照。后续的渲染会基于新旧虚拟DOM的快照进行比较。这个对比的过程其实就是diff算法。

diff算法的作用是,通过计算第一次和第二次 vdom 中真正变化的部分,从而只是针对变化的部分进行更新渲染,避免性能浪费。

2、diff算法的三大策略

  • tree diff(层级对比):React对虚拟DOM树进行分层比较,同层级的节点进行比较,不同层级的节点只进行创建或删除操作。
  • component diff(组件对比):对于同一类型的组件,直接比较它们的虚拟DOM;不同类型的组件则视为脏组件,需要重新创建或删除。
  • element diff(节点对比):对同一层级的元素进行比较,通过分配唯一的key值来识别节点,从而实现高效的更新。

3、tree diff

tree diff 是diff算法的基础策略。它的重点在于同层比较,忽略跨层级的移动。它遍历虚拟DOM树,并在必要时执行删除或插入操作。

也就是说,只对相同层级的DOM节点进行比较(即同一个父节点下的所有子节点)。不同级的(只有创建节点和删除节点的操作,官方不建议跨层级操作)。

对 vdom 树进行依次遍历,完成整个树的对比。一旦发现某处节点不存在,直接删除该节点以及之下的所有子节点。

4、component diff

组件间对比时,也有tree diff中策略。component diff比较同一类型的组件,有以下三种情况:

  1. 同一类型的组件遵循tree diff进行层级比较。
  2. 不同类型的组件被视为脏组件,需要重新创建或删除。
  3. 对于组件的更新,可以通过shouldComponentUpdate来优化,避免不必要的diff操作。

5、element diff

element diff针对同一层级的元素进行比较,也有tree diff中策略。具体有以下三种操作:

  1. 插入操作:对于新的节点,创建新节点并插入到正确的位置。
  2. 删除操作:删除不再需要的节点。
    • 组件新集合中有组件旧集合中的类型,但对应的element不可更新,只能执行删除。
    • 旧组件不在新集合里面,执行删除。
  3. 移动操作:如果元素移动到了新的位置,执行移动操作。

在 diff 中,对新旧的对比双方都添加了唯一的key值进行区分,用于判断key值对应的元素有没有改变。

通过这些策略,diff算法能够有效地减少不必要的DOM操作,从而提高性能和用户体验。