Le contenu de cet article concerne l'explication de l'algorithme dom virtuel et diff dans React (avec code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. Vous avez aidé.
Dom virtuel
Jsx écrit du HTML en surface, mais exécute en fait un morceau de js en interne
createElement
React.createElement( type, [props], [...children] )
createElement stocke cette structure arborescente en mémoire à l'intérieur
Jsx stocke enfin les objets en mémoire de manière récursive et exécute l'algorithme diff.
Structure multicouche
Simple createElement implémente
reactElement - ce qui est généré est un objet pour décrire ce nœud
réagir diff
La différence entre réagir diff et diff d'arbre traditionnel
Calculer les opérations minimales requises pour convertir une structure arborescente en une autre structure arborescente est un problème complexe et digne de recherche. L'algorithme de comparaison traditionnel compare les nœuds séquentiellement via la récursion de boucle, ce qui est inefficace et la complexité de l'algorithme atteint O(n^3)
stratégie de réaction de comparaison
Il existe très peu d'opérations de déplacement entre niveaux des nœuds DOM dans l'interface utilisateur Web et peuvent être ignorées.
Deux composants avec la même classe généreront des arborescences similaires, et deux composants avec des classes différentes généreront des arborescences différentes.
Pour un groupe de nœuds enfants de même niveau, ils peuvent être distingués par un identifiant unique.
diff d'arbre
Sur la base de la première stratégie, les arbres sont comparés hiérarchiquement. Deux arbres ne compareront que les nœuds du même niveau.
React utilise updateDepth pour effectuer un contrôle hiérarchique de l'arborescence Virtual DOM, tous les nœuds enfants sous le même nœud parent.
Quelle est l'opération de mouvement inter-niveaux des nœuds DOM ?
L'ensemble du nœud A (y compris ses nœuds enfants) est déplacé vers le nœud D
Si un nœud DOM apparaît Comment React diff fonctionnera-t-il dans les opérations de déplacement entre niveaux ?
React ne considérera simplement que les changements de position des nœuds de même niveau, alors que pour les nœuds de niveaux différents, il n'y a que des opérations de création et de suppression.
Lorsque le nœud racine découvre que A dans le nœud enfant a disparu, il détruira A directement ; lorsque D découvre qu'il y a un nœud enfant supplémentaire A, il créera un nouveau A (y compris l'enfant nœuds) comme nœud enfant. À l'heure actuelle, l'exécution de React diff est la suivante : créer A -> créer B -> créer C ->
Remarque :
Lors du développement de composants, le maintien d'une structure DOM stable contribuera à améliorer les performances. Par exemple, vous pouvez masquer ou afficher des nœuds via CSS sans supprimer ni ajouter de nœuds DOM.
diff des composants
Selon la stratégie deux
S'il s'agit du même type de composants, continuez à comparer selon l'arborescence DOM virtuelle de la stratégie originale.
Sinon, le composant sera jugé comme un composant sale, remplaçant ainsi tous les nœuds enfants sous l'ensemble du composant.
Pour le même type de composant, il se peut qu'il n'y ait aucun changement dans son DOM virtuel. Si vous pouvez en être sûr, vous pouvez économiser beaucoup de temps de fonctionnement des différences, donc React le permet. les utilisateurs doivent transmettre ShouldComponentUpdate() pour déterminer si le composant doit être comparé.
React détermine que D et G sont des types de composants différents, il ne comparera donc pas les structures des deux, mais supprimera directement le composant D et recréera le composant G et ses nœuds enfants , même si D Très similaire à la structure de G
élément diff
Lorsque les nœuds sont à au même niveau, React diff Trois opérations de nœuds sont fournies : INSERT_MARKUP (insertion), MOVE_EXISTING (mouvement) et REMOVE_NODE (suppression).
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 结构会有助于性能的提升;
Il est recommandé que pendant le processus de développement, les opérations telles que le déplacement du dernier nœud en tête de liste soient minimisées lorsque le nombre de nœuds est trop grand ou que les opérations de mise à jour sont trop fréquentes, cela affectera React dans une certaine mesure.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!