首页 > 专栏 > 前端 > 文章详情
React 的 Diffing 算法的一些规则 发布于:2021-03-27 12:20:59   来源:React   查看:4  讨论:0
React是对比创建的DOM树,和更新的DOM树的,两者差别。做出更新。其中涉及的规则有下:
  1. 两个不同类型的元素会产生出不同的树;
  2. 开发者可以通过设置 key 属性,来告知渲染哪些子元素在不同的渲染下可以保存不变;

当对比两棵树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。of3易塔云建站-模板下载,web开发资源,技术博客
of3易塔云建站-模板下载,web开发资源,技术博客
A:对比不同类型的元素of3易塔云建站-模板下载,web开发资源,技术博客

当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树。举个例子,当一个元素从 <a> 变成 <img>,从 <Article> 变成 <Comment>,或从 <Button> 变成 <div> 都会触发一个完整的重建流程。of3易塔云建站-模板下载,web开发资源,技术博客

当卸载一棵树时,对应的 DOM 节点也会被销毁。组件实例将执行 componentWillUnmount() 方法。当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中。组件实例将执行 UNSAFE_componentWillMount() 方法,紧接着 componentDidMount() 方法。所有与之前的树相关联的 state 也会被销毁。of3易塔云建站-模板下载,web开发资源,技术博客

在根节点以下的组件也会被卸载,它们的状态会被销毁。比如,当比对以下更变时:of3易塔云建站-模板下载,web开发资源,技术博客

<div>
  <Counter />
</div>

<span>
<Counter />
</span>
React 会销毁 Counter 组件并且重新装载一个新的组件。of3易塔云建站-模板下载,web开发资源,技术博客
of3易塔云建站-模板下载,web开发资源,技术博客
B:对比同一类型的元素of3易塔云建站-模板下载,web开发资源,技术博客
当对比两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。比如:
<div className="before" title="stuff" />

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

通过对比这两个元素,React 知道只需要修改 DOM 元素上的 className 属性。of3易塔云建站-模板下载,web开发资源,技术博客

当更新 style 属性时,React 仅更新有所更变的属性。比如:of3易塔云建站-模板下载,web开发资源,技术博客

<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

通过对比这两个元素,React 知道只需要修改 DOM 元素上的 color 样式,无需修改 fontWeightof3易塔云建站-模板下载,web开发资源,技术博客

在处理完当前节点之后,React 继续对子节点进行递归。of3易塔云建站-模板下载,web开发资源,技术博客

C:对比同一类型的组件元素

当一个组件更新时,组件实例会保持不变,因此可以在不同的渲染时保持 state 一致。React 将更新该组件实例的 props 以保证与最新的元素保持一致,并且调用该实例的 UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate() 以及 componentDidUpdate() 方法。of3易塔云建站-模板下载,web开发资源,技术博客

下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归。of3易塔云建站-模板下载,web开发资源,技术博客
of3易塔云建站-模板下载,web开发资源,技术博客
D:对子节点进行递归of3易塔云建站-模板下载,web开发资源,技术博客

默认情况下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。of3易塔云建站-模板下载,web开发资源,技术博客

在子元素列表末尾新增元素时,更新开销比较小。比如:of3易塔云建站-模板下载,web开发资源,技术博客

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

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

React 会先匹配两个 <li>first</li> 对应的树,然后匹配第二个元素 <li>second</li> 对应的树,最后插入第三个元素的 <li>third</li> 树。of3易塔云建站-模板下载,web开发资源,技术博客

如果只是简单的将新增元素插入到表头,那么更新开销会比较大。比如:of3易塔云建站-模板下载,web开发资源,技术博客

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

<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
React 并不会意识到应该保留 <li>Duke</li> 和 <li>Villanova</li>,而是会重建每一个子元素。这种情况会带来性能问题。of3易塔云建站-模板下载,web开发资源,技术博客
of3易塔云建站-模板下载,web开发资源,技术博客
E:keysof3易塔云建站-模板下载,web开发资源,技术博客
为了解决上述问题,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 的元素仅仅移动了。of3易塔云建站-模板下载,web开发资源,技术博客

实际开发中,编写一个 key 并不困难。你要展现的元素可能已经有了一个唯一 ID,于是 key 可以直接从你的数据中提取:of3易塔云建站-模板下载,web开发资源,技术博客

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

当以上情况不成立时,你可以新增一个 ID 字段到你的模型中,或者利用一部分内容作为哈希值来生成一个 key。这个 key 不需要全局唯一,但在列表中需要保持唯一。of3易塔云建站-模板下载,web开发资源,技术博客

最后,你也可以使用元素在数组中的下标作为 key。这个策略在元素不进行重新排序时比较合适,如果有顺序修改,diff 就会变慢。of3易塔云建站-模板下载,web开发资源,技术博客

当基于下标的组件进行重新排序时,组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用,如果 key 是一个下标,那么修改顺序时会修改当前的 key,导致非受控组件的 state(比如输入框)可能相互篡改,会出现无法预期的变动。of3易塔云建站-模板下载,web开发资源,技术博客

在 Codepen 有两个例子,分别为 展示使用下标作为 key 时导致的问题,以及不使用下标作为 key 的例子的版本,修复了重新排列,排序,以及在列表头插入的问题of3易塔云建站-模板下载,web开发资源,技术博客

 

评论

  • 匿名