>  기사  >  웹 프론트엔드  >  React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

不言
不言앞으로
2018-10-18 17:04:293345검색

이 기사의 내용은 React의 가상 DOM 및 diff 알고리즘에 대한 설명입니다. 필요한 참조 가치가 있습니다. 그것은 당신을 도와줍니다.

가상 돔

Jsx는 표면에 html을 작성하지만 실제로는 내부적으로 js 조각을 실행
createElement

React.createElement(
  type,
  [props],
  [...children]
)

createElement를 생성합니다. 이 트리 Shape 구조는 메모리에 저장됩니다
Jsx는 최종적으로 이러한 객체를 메모리에 재귀적으로 저장하고 diff 알고리즘을 실행합니다.

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

다층구조

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)#🎜 🎜 ## ## ## ####단순한 CreateElement 구현#### ## ## ## ## ## ## ###### 🎜🎜#reactElement - 이 노드를 설명하는 객체를 생성합니다. react diff

전통 트리 diff의 차이점

#🎜 🎜#한 트리 구조를 다른 트리 구조로 변환 계산 트리 구조의 최소 작업은 복잡하고 연구할 가치가 있는 문제입니다. 기존 diff 알고리즘은 루프 재귀를 통해 노드를 순차적으로 비교하는데 이는 비효율적이며 알고리즘 복잡도는 O(n^3)React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

react diff strategy

#🎜 🎜#

웹 UI에는 DOM 노드의 수준 간 이동 작업이 거의 없으므로 무시할 수 있습니다.

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)동일한 클래스를 가진 두 구성 요소는 유사한 트리 구조를 생성하고, 다른 클래스를 가진 두 구성 요소는 서로 다른 트리 구조를 생성합니다.

동일한 수준의 하위 노드 그룹의 경우 고유 ID로 구분할 수 있습니다.

tree diff

전략 1에 따라 두 트리의 계층적 비교만 수행합니다. 동일한 수준의 노드를 비교합니다.

React는 updateDepth를 사용하여 가상 DOM 트리, 동일한 상위 노드 아래의 모든 하위 노드에서 계층적 제어를 수행합니다.

  • DOM 노드의 교차 레벨 이동 작업이란 무엇입니까?

  • 전체 A 노드(하위 노드 포함)가 D 노드로 이동됩니다
  • #🎜 🎜 #

    DOM 노드의 레벨 간 이동 작업이 있는 경우 React diff는 어떻게 작동하나요?
React는 동일한 레벨의 노드 위치 변경만 고려하지만, 다른 레벨의 노드는 생성 및 삭제 작업만 수행합니다.

루트 노드는 하위 노드의 A가 사라진 것을 발견하면 A를 직접 파괴합니다. D가 하위 노드 A가 하나 더 있음을 발견하면 새로운 A를 생성합니다. (하위 노드 포함)을 하위 노드로 사용합니다. 이때 React diff의 실행은 create A -> create B -> create C -> delete A입니다.

참고:

컴포넌트를 개발할 때 안정적인 DOM 구조를 유지하면 성능 향상에 도움이 됩니다. 예를 들어 DOM 노드를 실제로 제거하거나 추가하지 않고도 CSS를 통해 노드를 숨기거나 표시할 수 있습니다.

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)구성 요소 차이

두 번째 전략 기반


동일한 유형의 구성 요소인 경우 원래 전략에 따라 가상 DOM 트리를 계속 비교합니다. React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

그렇지 않은 경우 해당 구성 요소는 더티 구성 요소로 판단되어 전체 구성 요소 아래의 모든 하위 노드를 교체합니다.


동일한 유형의 컴포넌트에 대해서는 Virtual DOM에 변경 사항이 없을 수 있습니다. 이를 확실히 알면 diff 작업 시간을 많이 절약할 수 있습니다. 따라서 React에서는 사용자가 shouldComponentUpdate()를 사용하여 구성 요소를 비교해야 하는지 여부를 결정할 수 있습니다.

React는 D와 G가 서로 다른 유형의 구성 요소라고 판단하므로 구조를 비교하지 않고 구성 요소 D를 직접 삭제하고 구성 요소 G와 해당 하위 노드를 다시 만듭니다. , D와 G의 구조가 매우 유사하더라도 🎜#

노드가 동일한 레벨에 있을 때 React diff는 INSERT_MARKUP(삽입), MOVE_EXISTING(이동) 및 REMOVE_NODE(삭제)라는 세 가지 노드 작업을 제공합니다.

  • 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의 가상 dom 및 diff 알고리즘 설명(코드 포함)

带来的问题:都是相同的节点,但由于位置发生变化,导致需要进行繁杂低效的删除、创建操作,其实只要对这些节点进行位置移动即可

react优化策略:允许开发者对同一层级的同组子节点,添加唯一 key 进行区分

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

优化后diff实现:

  1. 对新集合的节点进行循环遍历,通过唯一 key 可以判断新老集合中是否存在相同的节点

  2. 如果存在相同节点,则进行移动操作,但在移动前需要将当前节点在老集合中的位置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 又是如何对比运作的呢?

React의 가상 dom 및 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,则创建新节点 E
    lastIndex不做处理
    E 的位置更新为新集合中的位置,nextIndex++
删除:当完成新集合中所有节点 diff 时,最后还需要对老集合进行循环遍历,判断是否存在新集合中没有但老集合中仍存在的节点,发现存在这样的节点 D,因此删除节点 D

react diff的问题

理论上 diff 应该只需对 D 执行移动操作,然而由于 D 在老集合的位置是最大的,导致其他节点的 _mountIndex

React의 가상 dom 및 diff 알고리즘 설명(코드 포함)

建议:在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。

总结:

  • React 通过制定大胆的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;

  • React 通过分层求异的策略,对 tree diff 进行算法优化;

  • React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;

  • React 通过设置唯一 key的策略,对 element diff 进行算法优化;

  • 建议,在开发组件时,保持稳定的 DOM 结构会有助于性能的提升;

  • 개발 과정에서 마지막 노드를 목록의 선두로 이동하는 등의 작업은 최소화하는 것이 좋습니다. 노드 수가 너무 많거나 업데이트 작업이 너무 빈번하면 렌더링 성능에 영향을 미칠 수 있습니다. 어느 정도 반응합니다.

위 내용은 React의 가상 dom 및 diff 알고리즘 설명(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제