This article brings you an explanation of the virtual dom and diff algorithm in React (with code). It has certain reference value. Friends in need can refer to it. I hope it will help You helped.
Virtual dom
Jsx writes html on the surface, but actually executes a piece of js internally
createElement
React.createElement( type, [props], [...children] )
createElement stores this tree structure in memory
Jsx finally stores such objects recursively in the memory and executes the diff algorithm.
Multi-layer structure
Simple createElement implements
reactElement - an object is generated to describe this node
react diff
The difference between diff of traditional trees
Calculating the minimum operations required to convert one tree structure into another tree structure is a complex and worthy of research problem. The traditional diff algorithm compares nodes sequentially through loop recursion, which is inefficient and the algorithm complexity reaches O(n^3)
react diff strategy
The cross-level movement operations of DOM nodes in Web UI are very few and can be ignored.
Two components with the same class will generate similar tree structures, and two components with different classes will generate different tree structures.
For a group of child nodes at the same level, they can be distinguished by a unique id.
tree diff
Based on strategy one, trees are compared hierarchically. Two trees will only compare nodes at the same level.
React uses updateDepth to perform hierarchical control on the Virtual DOM tree, all child nodes under the same parent node.
What is the cross-level movement operation of DOM nodes?
The entire A node (including its child nodes) is moved to the D node
If a DOM node appears How will React diff perform in cross-level move operations?
React will only simply consider the position changes of nodes at the same level, while for nodes at different levels, there are only creation and deletion operations.
When the root node finds that A in the child node has disappeared, it will destroy A directly; when D finds that there is an additional child node A, it will create a new A (including child nodes) as its child. node. At this time, the execution of React diff is: create A -> create B -> create C -> delete A.
Note:
When developing components, maintaining a stable DOM structure will help improve performance. For example, you can hide or show nodes via CSS without actually removing or adding DOM nodes.
component diff
Based on strategy two
If they are components of the same type, continue to compare according to the original strategy virtual DOM tree.
If not, the component will be judged as a dirty component, thereby replacing all child nodes under the entire component.
For the same type of component, there may be no changes in the Virtual DOM. If you can know this for sure, you can save a lot of diff operation time. Therefore, React allows users to pass shouldComponentUpdate( ) to determine whether the component needs to be diffed.
React determines that D and G are different types of components, so it will not compare the structures of the two, but directly delete component D and re-create component G and its child nodes, even if D Very similar to the structure of G
##element diff
#When nodes are at the same level, React diff Three node operations are provided: INSERT_MARKUP (insertion), MOVE_EXISTING (movement) and REMOVE_NODE (deletion).INSERT_MARKUP,新的 component 类型不在老集合里, 即是全新的节点,需要对新节点执行插入操作。
MOVE_EXISTING,在老集合有新 component 类型,且 element 是可更新的类型,generateComponentChildren 已调用 receiveComponent,这种情况下 prevChild=nextChild,就需要做移动操作,可以复用以前的 DOM 节点。
REMOVE_NODE,老 component 类型,在新集合里也有,但对应的 element 不同则不能直接复用和更新,需要执行删除操作,或者老 component 不在新集合里的,也需要执行删除操作。
eg: 新老集合进行 diff 差异化对比,发现 B != A,则创建并插入 B 至新集合,删除老集合 A;以此类推,创建并插入 A、D 和 C,删除 B、C 和 D。
带来的问题:都是相同的节点,但由于位置发生变化,导致需要进行繁杂低效的删除、创建操作,其实只要对这些节点进行位置移动即可
react优化策略:允许开发者对同一层级的同组子节点,添加唯一 key 进行区分
优化后diff实现:
对新集合的节点进行循环遍历,通过唯一 key 可以判断新老集合中是否存在相同的节点
如果存在相同节点,则进行移动操作,但在移动前需要将当前节点在老集合中的位置child._mountIndex与lastIndex(访问过的节点在老集合中最右的位置即最大的位置)进行比较,if (child._mountIndex
分析:
element _mountIndex lastIndex nextIndex enqueueMove B 1 0 0 false A 0 1 1 true D 3 1 2 false C 2 3 3 true
step:
从新集合中取得 B,判断老集合中存在相同节点 B
B 在老集合中的位置 B._mountIndex = 1
初始 lastIndex = 0
不满足 child._mountIndex 更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex) lastIndex更新为1
将 B 的位置更新为新集合中的位置prevChild._mountIndex = nextIndex,此时新集合中 B._mountIndex = 0,nextIndex++
以上主要分析新老集合中存在相同节点但位置不同时,对节点进行位置移动的情况,如果新集合中有新加入的节点且老集合存在需要删除的节点,那么 React diff 又是如何对比运作的呢?
element _mountIndex lastIndex nextIndex enqueueMove B 1 0 0 false E no exist C 2 1 2 false A 0 2 3 true
step
新建:从新集合中取得 E,判断老集合中不存在相同节点 E,则创建新节点 Ereact diff的问题
理论上 diff 应该只需对 D 执行移动操作,然而由于 D 在老集合的位置是最大的,导致其他节点的 _mountIndex
建议:在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。
总结:
React 通过制定大胆的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;
React 通过分层求异的策略,对 tree diff 进行算法优化;
React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;
React 通过设置唯一 key的策略,对 element diff 进行算法优化;
建议,在开发组件时,保持稳定的 DOM 结构会有助于性能的提升;
It is recommended that during the development process, operations such as moving the last node to the head of the list should be minimized. When the number of nodes is too large or update operations are too frequent, it will affect React to a certain extent. Rendering performance.
The above is the detailed content of Explanation of virtual dom and diff algorithm in React (with code). For more information, please follow other related articles on the PHP Chinese website!