React 中 Virtual DOM 与 Diffing 算法的关系

时间: 2019-07-27阅读: 114标签: 算法

Virtual DOM 是一种编程理念

Virtual DOM 是一种编程理念。UI 信息被特定语言描述并保存到内存中,再通过特定的库,例如 ReactDOM 与真实的 DOM 同步信息。这一过程成为 协调 (Reconciliation)。


与之对应的数据结构

Virtual DOM 反映到实际的数据结构上,就是每一个 React 的 fiber node

// UI 组件描述
const Span = (props) => <span></span>

// 实际的 Fiber node structure
{
  stateNode: new HTMLSpanElement,
  type: "span",
  alternate: null,
  key: null,
  updateQueue: null,
  memoizedState: null,
  pendingProps: {},
  memoizedProps: {},
  tag: 1,
  effectTag: 0,
  nextEffect: null
}

这一抽离结构有点像 React 版本的 AST 抽象语法树。


Diffing 算法

问题

在 Virtual DOM -> Real DOM 之间的转换过程中,需要高效率的算法来支撑。由于某个时刻调用 React render() 方法生成的 React 元素组成的树,与下一次 state 或 props 变化时调用同一个 render 返回的树是不一样的,React 需要根据这两个不同的树来决定如何高效地让最新的 Virtual DOM 反应到真实 DOM 中。

解决方式

Diffing 算法就是解决如何更有效率地更新 UI 的关键。

React 采取了一个复杂度为 O(n) 的比较策略,这个策略有两个假设

  1. 两个不同类型的元素会产出不同的树
  2. 开发者可以通过 key prop 来保持元素的稳定


Diffing 策略

1. 对比根节点的元素

如果为不同类型,React 将会把原有的树拆卸并重新建立新的树。例如 <div> -> <span>。

  1. 当这颗树被拆卸后,对应的 DOM 节点也被销毁,组件实例回调用 willUnmount 方法。
  2. 当建立新的树的时候,对应的 DOM 将被插入到 DOM 中,并调用 didMount 方法。

在根节点以下的组件也会被卸载,它们的状态会被销毁。例如:

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>


2. 对比同一类型的元素

当对比两个同类型的 React 元素时,React 会保留 DOM 节点,仅对比以及更新有变化的属性

<div className="before" title="stuff" />

<div className="after" title="stuff" />

通过对比两个元素,React 得知 className 变化,所以只需要更新 DOM 对应元素上的 class。

当处理完当前节点时,React 将会对子节点进行递归。


3. 对比同类型的组件元素

当一个 React 组件需要更新时(例如 props 有变化),组件实例保持不变,实例中的 state 能在不同渲染时保持一致。React 将更新该组件实例的 props 以保持与最新的元素的一致。并调用 该实例的原型 上的函数 getDerivedStateFromProps(官方文档是 componentWillReceiveProps 和 componentWillUpdate,但这将会被弃用)。

下一步是调用该实例的 render 方法,diffing 算法将在之前的结果和最新的结果中进行递归。


4. 对子节点进行递归

问题

在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表,当发现两个子元素有差异时,将生成一个「变种(mutation)」。

例如在子元素列表末尾新增元素时,更变开销比较小。比如:

// before
<ul>
  <li>first</li>
  <li>second</li>
</ul>

// after
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

React 会匹配两个 <li>first</li> 对应的树、两个 <li>second</li> 对应的树,然后插入 <li>third</li> 树。

但如果就这样简单实现的话,那么在列表头部插入会很影响性能,更变的开销会比较大。比如:

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React 会认为每个子元素都「改变(mutate)」了,而不会认为可以保持 <li>Duke</li> 和 <li>Villanova</li> 子树不变,从而导致重新渲染。这种情况下的低效可能会带来性能问题。

解决策略 Keys

为了解决以上问题,React 支持 key 属性。当子传入 key 到子元素时,React 通过 key 来匹配比较原有树上的子元素以及最新树上的子元素的差异。以下例子在新增 key 之后使得之前的低效转换变得高效:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

现在 React 知道只有带着 '2014' key 的元素是新元素,带着 '2015' 以及 '2016' key 的元素仅仅移动了位置。

所以一般在开发的时候最好使用一个有唯一属性的 id 来作为 key

<li key={item.id}>{item.name}</li>

在开发者自己确定数组数据不会轻易改变的情况下才可以用数组下表来作为 key。


权衡(Tradeoffs)

上述只是 协调算法(reconciliation algorithm)的实现细节而已。React 可以响应每一次 action 后重新渲染整个应用,最终结果也会是一样的。

需要明确知道的是,在当前上下文(this context)中重新渲染(rerender)意味着会调用所有的 component 的 render(),但并不意味着 React 会卸载(unmount)或重载(remount)它们。它(协调算法)只会用上述规则在其过程中找出不同。


吐血推荐

1.站长广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入...

2.休闲娱乐: 网页游戏  直播/交友   H5游戏

链接: http://www.fly63.com/article/detial/4979

六种排序算法的Js实现

本文将介绍数据排序的基本算法和高级算法。这些算法都只依赖数组来存储数据。数组测试平台首先我们构造一个数组测试平台类,这些算法非常逼真地模拟了人类在现实生活中对数据的排序。

数据结构与算法之绪论

什么是数据结构?简单来说可以解释为:程序设计=数据结构+算法;主要是用来研究数据结构的关系,数据元素之间存在的一种或多种特定关系的集合;

RSA 背后的算法

随着科技发展,计算能力越来越强,特别是量子计算的兴起,我们对超大质数的位数要求也越来越高,512 bit 的 RSA 已经被破解,而 1024 bit 也已经摇摇欲坠,现阶段 2048 bit 长度还是安全的,可是未来,谁又知道呢?

一些常用的语音特征提取算法

语言是一种复杂的自然习得的人类运动能力。成人的特点是通过大约100块肌肉的协调运动,每秒发出14种不同的声音。说话人识别是指软件或硬件接收语音信号,识别语音信号中出现的说话人,然后识别说话人的能力

JavaScript十大排序必修算法

冒泡排序通过相邻元素的比较和交换,使得每一趟循环都能找到未有序数组的最大值或最小值,双向冒泡普通的冒泡排序在一趟循环中只能找出一个最大值或最小值,双向冒泡则是多一轮循环既找出最大值也找出最小

js实现1万的阶乘

但是这样就会存在问题,Js中最大的安全整数为2^53- 1,10000!结果溢出该范围,代码运行结果为Infinity,无法计算出正确的结果。那么如何才能计算大数据的阶乘呢?

js中常用的基础算法

今天给大家分享一下js中常用的基础算法,废话不多说,直接上代码;两个数字调换顺序;对象排序,安装对象中的id排序对象的位置;冒泡排序 ;随机出现不同的数字;字符串大小写互换

原生js数值开根算法

理论上来讲,开根后的值为x,那么x^2=n,即可以将其转换为数学问题,令y=x^2-n,那么只需要求方程与x轴正方向的焦点就可以得出想要的结果,我们作x=a与方程交于(a^2-n),求得他的切线与x轴的交点

程序员必知必会的10 大基础算法!

快速排序算法:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个项目要Ο(nlogn)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(nlogn)算法更快

Js算法模式:动态规划和贪心算法

动态规划(Dynamic Programming,DP)是一种将复杂问题分解成更小的子问题来解决的优化算法。下面有一些用动态规划来解决实际问题的算法:给定一组硬币的面额,以及要找零的钱数,计算出符合找零钱数的最少硬币数量。

fly63.com版权所有,内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权,请与小编联系!情况属实本人将予以删除!

广告赞助文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全