You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
React provides a declarative API so that you don't have to worry about exactly what changes on every update. This makes writing applications a lot easier, but it might not be obvious how this is implemented within React. This article explains the choices we made in React's "diffing" algorithm so that component updates are predictable while being fast enough for high-performance apps.
7
+
React 提供的声明式 API 让开发者可以在对 React 的底层实现没有具体了解的情况下编写应用。在开发者编写应用时虽然保持相对简单的心智,但开发者无法了解内部的实现机制。本文描述了在实现 React 的 "diffing" 算法中我们做出的设计决策以保证组件满足更新具有可预测性,以及在繁杂业务下依然保持应用的高性能性。
8
8
9
-
## Motivation {#motivation}
9
+
## 设计动力 {#motivation}
10
10
11
-
When you use React, at a single point in time you can think of the `render()` function as creating a tree of React elements. On the next state or props update, that `render()` function will return a different tree of React elements. React then needs to figure out how to efficiently update the UI to match the most recent tree.
这个算法问题有一些通用的解决方案,即生成将一棵树转换成另一棵树的最小操作数。 然而,即使在[最前沿的算法中](http://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf),该算法的复杂程度为 O(n<sup> 3 </sup>),其中 n 是树中元素的数量。
12
14
13
15
There are some generic solutions to this algorithmic problem of generating the minimum number of operations to transform one tree into another. However, the [state of the art algorithms](http://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf) have a complexity in the order of O(n<sup>3</sup>) where n is the number of elements in the tree.
14
16
15
-
If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. Instead, React implements a heuristic O(n) algorithm based on two assumptions:
1.Two elements of different types will produce different trees.
18
-
2.The developer can hint at which child elements may be stable across different renders with a `key` prop.
19
+
1.两个不同类型的元素会产生出不同的树;
20
+
2.开发者可以通过 `key` prop 来暗示哪些子元素在不同的渲染下能保持稳定;
19
21
20
-
In practice, these assumptions are valid for almost all practical use cases.
22
+
在实践中,我们发现以上假设在几乎所有实用的场景下都成立。
21
23
22
-
## The Diffing Algorithm {#the-diffing-algorithm}
24
+
## Diffing 算法 {#the-diffing-algorithm}
23
25
24
-
When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.
26
+
当对比两颗树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。
25
27
26
-
### Elements Of Different Types {#elements-of-different-types}
28
+
### 比对不同类型的元素 {#elements-of-different-types}
27
29
28
-
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from `<a>`to`<img>`, or from `<Article>`to`<Comment>`, or from `<Button>`to`<div>`- any of those will lead to a full rebuild.
When tearing down a tree, old DOM nodes are destroyed. Component instances receive `componentWillUnmount()`. When building up a new tree, new DOM nodes are inserted into the DOM. Component instances receive `componentWillMount()`and then `componentDidMount()`. Any state associated with the old tree is lost.
32
+
当拆卸一颗树时,对应的 DOM 节点也会被销毁。组件实例将执行 `componentWillUnmount()` 方法。当建立一颗新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中。组件实例将执行 `componentWillMount()`方法,紧接着 `componentDidMount()` 方法。所有跟之前的树所关联的 state 也会被销毁。
31
33
32
-
Any components below the root will also get unmounted and have their state destroyed. For example, when diffing:
34
+
在根节点以下的组件也会被卸载,它们的状态会被销毁。比如,当比对以下更变时:
33
35
34
36
```xml
35
37
<div>
@@ -41,43 +43,43 @@ Any components below the root will also get unmounted and have their state destr
41
43
</span>
42
44
```
43
45
44
-
This will destroy the old `Counter`and remount a new one.
46
+
React 会销毁 `Counter`组件并且重新装载一个新的组件。
45
47
46
-
### DOM Elements Of The Same Type {#dom-elements-of-the-same-type}
48
+
### 比对同一类型的元素 {#dom-elements-of-the-same-type}
47
49
48
-
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:
50
+
当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。比如:
49
51
50
52
```xml
51
53
<divclassName="before"title="stuff" />
52
54
53
55
<divclassName="after"title="stuff" />
54
56
```
55
57
56
-
By comparing these two elements, React knows to only modify the `className`on the underlying DOM node.
58
+
通过比对这两个元素,React 知道只需要修改 DOM 元素上的 `className`属性。
57
59
58
-
When updating `style`, React also knows to update only the properties that changed. For example:
When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls `componentWillReceiveProps()`and`componentWillUpdate()`on the underlying instance.
74
+
当一个组件更新时,组件实例保持不变,这样 state 在跨越不同的渲染时保持一致。React 将更新该组件实例的 props 以跟最新的元素保持一致,并且调用该实例的 `componentWillReceiveProps()`和`componentWillUpdate()`方法。
73
75
74
-
Next, the `render()`method is called and the diff algorithm recurses on the previous result and the new result.
76
+
下一步,调用 `render()`方法,diff 算法将在之前的结果以及新的结果中进行递归。
75
77
76
-
### Recursing On Children {#recursing-on-children}
78
+
### 对子节点进行递归 {#recursing-on-children}
77
79
78
-
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there's a difference.
80
+
在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。
79
81
80
-
For example, when adding an element at the end of the children, converting between these two trees works well:
82
+
在子元素列表末尾新增元素时,更变开销比较小。比如:
81
83
82
84
```xml
83
85
<ul>
@@ -92,9 +94,9 @@ For example, when adding an element at the end of the children, converting betwe
92
94
</ul>
93
95
```
94
96
95
-
React will match the two `<li>first</li>`trees, match the two `<li>second</li>`trees, and then insert the `<li>third</li>`tree.
If you implement it naively, inserting an element at the beginning has worse performance. For example, converting between these two trees works poorly:
99
+
如果简单实现的话,那么在列表头部插入会很影响性能,那么更变开销会比较大。比如:
98
100
99
101
```xml
100
102
<ul>
@@ -109,11 +111,11 @@ If you implement it naively, inserting an element at the beginning has worse per
109
111
</ul>
110
112
```
111
113
112
-
React will mutate every child instead of realizing it can keep the `<li>Duke</li>`and`<li>Villanova</li>`subtrees intact. This inefficiency can be a problem.
In order to solve this issue, React supports a `key`attribute. When children have keys, React uses the key to match children in the original tree with children in the subsequent tree. For example, adding a `key`to our inefficient example above can make the tree conversion efficient:
In practice, finding a key is usually not hard. The element you are going to display may already have a unique ID, so the key can just come from your data:
When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. The key only has to be unique among its siblings, not globally unique.
141
+
当以上情况不成立时,你可以新增一个 ID 字段到你的模型中,或者利用一部分内容作为哈希值来生成一个 key。这个 key 不需要全局唯一,但在列表中需要保持唯一。
140
142
141
-
As a last resort, you can pass an item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
Reorders can also cause issues with component state when indexes are used as keys. Component instances are updated and reused based on their key. If the key is an index, moving an item changes it. As a result, component state for things like uncontrolled inputs can get mixed up and updated in unexpected ways.
145
+
当基于下标的组件进行重新排序时,组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用,如果 key 是一个下标,那么修改顺序时会修改当前的 key,导致非受控组件的 state(比如输入框)可能相互篡改导致无法预期的变动。
144
146
145
-
[Here](codepen://reconciliation/index-used-as-key)is an example of the issues that can be caused by using indexes as keys on CodePen, and [here](codepen://reconciliation/no-index-used-as-key)is an updated version of the same example showing how not using indexes as keys will fix these reordering, sorting, and prepending issues.
It is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same. Just to be clear, rerender in this context means calling `render`for all components, it doesn't mean React will unmount and remount them. It will only apply the differences following the rules stated in the previous sections.
We are regularly refining the heuristics in order to make common use cases faster. In the current implementation, you can express the fact that a subtree has been moved amongst its siblings, but you cannot tell that it has moved somewhere else. The algorithm will rerender that full subtree.
Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.
155
+
由于 React 依赖启发性算法,如果以下假设没有得到满足,性能会有所损耗。
154
156
155
-
1.The algorithm will not try to match subtrees of different component types. If you see yourself alternating between two component types with very similar output, you may want to make it the same type. In practice, we haven't found this to be an issue.
2. Keys should be stable, predictable, and unique. Unstable keys (like those produced by `Math.random()`) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
159
+
2. Keys 应该具有稳定,可预测,以及列表内唯一的特质。不稳定的 key(比如通过 `Math.random()` 生成的)会导致许多组件实例和 DOM 节点被不必要地重新创建,这可能导致性能下降和子组件中的状态丢失。
0 commit comments