Cet article donne principalement une explication détaillée des principes du framework react. Il y a également beaucoup de compréhension approfondie de React ci-dessous. Jetons un coup d'œil à cet article maintenant
.
Je travaille sur React depuis plus de 2 ans. J'aime et déteste ce framework. Tout le monde connaît ses avantages, mais ses défauts sont progressivement révélés. grand projet , lorsqu'il est combiné avec des frameworks tiers tels que
Redux
etReactRouter
, la quantité de code métier complexe deviendra très importante (le code frontal est souvent 1,5 fois la taille précédente). Si la conception sous-jacente n'est pas bonne au début, vous serez souvent confronté au problème d'une faible efficacité de développement. Ce qui suit résume quelques concepts fondamentaux du framework React, j'espère qu'il sera utile à tout le monde :
L'algorithme diff
de React est Virtual DOM
La raison pour laquelle la volonté est la plus grande confiance est que nous savons tous que les performances d'une page sont généralement déterminées par la vitesse de rendu et le nombre de rendus. Comment maximiser l'utilisation de l'algorithme diff
pour le développement ? Voyons d'abord comment cela fonctionne.
Calculez les opérations minimales requises pour convertir une structure arborescente en une autre structure arborescente. L'algorithme de comparaison traditionnel compare les nœuds de manière séquentielle via la récursion de boucle, ce qui est inefficace et complexe. O(n^3)
, où n est le nombre total de nœuds dans l'arborescence. Autrement dit, si vous souhaitez afficher 1 000 nœuds, vous devrez effectuer des milliards de comparaisons en séquence. Cette consommation de performances est inacceptable pour les projets front-end.
Comme vu ci-dessus, la complexité de l'algorithme de comparaison traditionnel est O(n^3)
, qui ne peut évidemment pas répondre aux exigences de performances. Et React
transforme les problèmes de O(n^3)
complexité en problèmes de O(n)
complexité en formulant des stratégies audacieuses. Comment a-t-il fait ?
Il existe très peu d'opérations de déplacement inter-niveaux des nœuds DOM dans l'interface utilisateur Web et peuvent être ignorées. React a réalisé une optimisation concise et claire de l'algorithme d'arbre, c'est-à-dire qu'une comparaison hiérarchique d'arbres ne comparera que les nœuds du même niveau. Comme le montre la figure ci-dessous :
React utilise updateDepth pour contrôler le niveau de l'arborescence DOM virtuelle. Seuls les nœuds DOM dans la même zone de couleur seront comparés. c'est-à-dire que la même couleur sera comparée. Tous les nœuds enfants sous un nœud parent. Lorsqu'il s'avère qu'un nœud n'existe plus, le nœud et ses sous-nœuds seront complètement supprimés et ne seront plus utilisés pour d'autres comparaisons. De cette façon, un seul parcours de l’arborescence est nécessaire pour terminer la comparaison de l’ensemble de l’arborescence DOM.
// tree diff算法实现updateChildren: function(nextNestedChildrenElements, transaction, context) { updateDepth++; var errorThrown = true; try { this._updateChildren(nextNestedChildrenElements, transaction, context); errorThrown = false; } finally { updateDepth--; if (!updateDepth) { if (errorThrown) { clearQueue(); } else { processQueue(); } } } }
Comme indiqué ci-dessous, le nœud A (y compris ses sous-nœuds) est entièrement déplacé vers le nœud D puisque React ne considérera que simplement la transformation de position des nœuds de même niveau, et pour les nœuds de niveaux différents. , il ne fait que les créer et les supprimer. Lorsque le nœud racine découvre que A a disparu dans le nœud enfant, il détruira directement A ; lorsque D découvre qu'il y a un nœud enfant supplémentaire A, il créera un nouveau A (y compris les nœuds enfants) comme nœud enfant. À l'heure actuelle, l'état d'exécution de React diff
est : créer A -> créer B -> créer C -> supprimer A.
On peut constater que lorsqu'un nœud se déplace à travers les niveaux, l'opération de mouvement imaginaire ne se produira pas, mais l'arbre avec A comme nœud racine sera complètement recréé. , une opération qui affecte les React
performances.
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.
S'il s'agit de composants du même type, continuez à comparer selon la stratégie d'origine virtual DOM tree
.
Sinon, le composant sera jugé comme dirty component
, remplaçant ainsi tous les nœuds enfants sous l'ensemble du composant.
Pour le même type de composant, il est possible que son Virtual DOM
ne présente aucun changement. Si vous pouvez en être sûr, vous pouvez gagner beaucoup de temps de fonctionnement des différences, donc. React
permet aux utilisateurs d'utiliser shouldComponentUpdate()
pour déterminer si le composant doit être comparé.
Comme indiqué ci-dessus, lorsque component D
est remplacé par component G
, même si les deux component
ont des structures similaires, une fois que React
détermine que D et G sont des types de composants différents, les structures des deux seront ne pas être comparé. Au lieu de cela, supprimez component D
directement et recréez component G
et ses nœuds enfants. Bien que lorsque deux component
sont de types différents mais ont des structures similaires, React diff
affectera les performances, mais comme le dit le blog officiel React
: Différents types de component
ont peu de chances d'être similaires DOM tree
, c'est donc Il est difficile pour des facteurs aussi extrêmes d’avoir un impact significatif sur le processus de développement.
Pour un groupe de nœuds enfants de même niveau, ils peuvent être distingués par un identifiant unique. React propose une stratégie d'optimisation : les développeurs sont autorisés à ajouter des clés uniques pour distinguer le même groupe de nœuds enfants au même niveau. Bien qu'il ne s'agisse que d'un petit changement, les performances ont subi des changements bouleversants !
Les nœuds contenus dans les nouvelles et anciennes collections sont comme indiqué dans la figure ci-dessous. Les nouvelles et anciennes collections sont comparées par diff. Grâce à la clé, on constate que les nœuds des nouvelles et anciennes collections sont. les mêmes nœuds, il n'est donc pas nécessaire de supprimer et de créer des nœuds, il vous suffit de déplacer les positions des nœuds dans l'ancien ensemble et de les mettre à jour avec les positions des nœuds dans le nouvel ensemble. Le résultat donné par React est le suivant : B et D n'effectuent aucune opération et A et C effectuent des opérations de déplacement.
(1)[basées sur les différences d'arborescence] Lors du développement de composants, le maintien d'une structure DOM stable permet de maintenir l'ensemble performance. En d’autres termes, effectuez le moins de manipulations dynamiques possible de la structure du DOM, en particulier les opérations de mouvement. Lorsque le nombre de nœuds est trop grand ou que la page est mise à jour trop souvent, le phénomène de gel de la page est plus évident. Vous pouvez masquer ou afficher des nœuds via CSS sans supprimer ni ajouter de nœuds DOM.
(2)[Basé sur les différences entre les composants] Lors du développement de composants, faites attention à utiliser shouldComponentUpdate()
pour réduire les mises à jour inutiles des composants. De plus, des structures similaires doivent être regroupées autant que possible en composants, ce qui réduit non seulement la quantité de code, mais réduit également la component diff
consommation de performances.
(3)[Basé sur la différence d'élément] Pour les structures de liste, essayez de réduire les opérations comme déplacer le dernier nœud en tête de la liste lorsque le nombre de nœuds est trop grand ou. les opérations de mise à jour sont trop fréquentes, cela affectera dans une certaine mesure les performances de rendu de React.
Le cycle de vie de React peut être divisé en quatre situations :
Lorsqu'il est chargé pour la première fois Lors de l'installation d'un composant, exécutez getDefaultProps
, getInitialState
, componentWillMount
, render
et componentDidMount
dans l'ordre
lors de la désinstallation d'un composant, exécutez < ; 🎜>;>componentWillUnmount
, getInitialState
et componentWillMount
sont exécutés dans l'ordre, mais render
n'est pas exécuté componentDidMount
; getDefaultProps
, componentWillReceiveProps
, shouldComponentUpdate
et componentWillUpdate
dans l'ordre. render
componentDidUpdate
, mountComponent
dans la vie cycle , getInitialState
et componentWillMount
. render
componentDidMount
Statut 2 : RECEIVE_PROPS
est chargé de gérer, updateComponent
, componentWillReceiveProps
, shouldComponentUpdate
et componentWillUpdate
. render
componentDidUpdate
Statut 3 : DÉMONTAGE
est chargé de gérer dans le cycle de vie. (Si vous voulez en savoir plus, rendez-vous sur le site Web PHP chinois unmountComponent
React Reference Manual componentWillUnmount
pour en savoir plus) Définissez d'abord le statut sur
existe, exécutez ; si à ce moment l'appel UNMOUNTING
dans componentWillUnmount
ne déclenchera pas componentWillUnmount
. L'état de la mise à jour est setState
et l'opération de désinstallation du composant est terminée. Le code d'implémentation est le suivant : reRender
// 卸载组件unmountComponent: function() { // 设置状态为 UNMOUNTING this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; // 如果存在 componentWillUnmount,则触发 if (this.componentWillUnmount) { this.componentWillUnmount(); } // 更新状态为 null this._compositeLifeCycleState = null; this._renderedComponent.unmountComponent(); this._renderedComponent = null; ReactComponent.Mixin.unmountComponent.call(this); }
生命周期 | 调用次数 | 能否使用setState() |
---|---|---|
getDefaultProps | 1 | 否 |
getInitialState | 1 | 否 |
componentWillMount | 1 | 是 |
render | >=1 | 否 |
componentDidMount | 1 | 是 |
componentWillReceiveProps | >=0 | 是 |
shouldComponentUpdate | >=0 | 否 |
componentWillUpdate | >=0 | 否 |
componentDidUpdate | >=0 | 否 |
componentWillUnmount | 1 | 否 |
componentDidUnmount | 1 | 否 |
setState
是React
框架的核心方法之一,下面介绍一下它的原理:
// 更新 statesetState: function(partialState, callback) { // 合并 _pendingState this.replaceState( assign({}, this._pendingState || this.state, partialState), callback ); },
当调用 setState
时,会对 state
以及 _pendingState
更新队列进行合并操作,但其实真正更新 state
的幕后黑手是replaceState
。
// 更新 statereplaceState: function(completeState, callback) { validateLifeCycleOnReplaceState(this); // 更新队列 this._pendingState = completeState; // 判断状态是否为 MOUNTING,如果不是,即可执行更新 if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) { ReactUpdates.enqueueUpdate(this, callback); } },
replaceState
会先判断当前状态是否为 MOUNTING
,如果不是即会调用 ReactUpdates.enqueueUpdate
执行更新。
当状态不为 MOUNTING
或 RECEIVING_PROPS
时,performUpdateIfNecessary
会获取 _pendingElement
、_pendingState
、_pendingForceUpdate
,并调用 updateComponent
进行组件更新。
// 如果存在 _pendingElement、_pendingState、_pendingForceUpdate,则更新组件performUpdateIfNecessary: function(transaction) { var compositeLifeCycleState = this._compositeLifeCycleState; // 当状态为 MOUNTING 或 RECEIVING_PROPS时,则不更新 if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING || compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) { return; } var prevElement = this._currentElement; var nextElement = prevElement; if (this._pendingElement != null) { nextElement = this._pendingElement; this._pendingElement = null; } // 调用 updateComponent this.updateComponent( transaction, prevElement, nextElement ); }
如果在
shouldComponentUpdate
或componentWillUpdate
中调用setState
,此时的状态已经从RECEIVING_PROPS -> NULL
,则performUpdateIfNecessary
就会调用updateComponent
进行组件更新,但updateComponent
又会调用shouldComponentUpdate
和componentWillUpdate
,因此造成循环调用,使得浏览器内存占满后崩溃。
不建议在 getDefaultProps
、getInitialState
、shouldComponentUpdate
、componentWillUpdate
、render
和 componentWillUnmount
中调用 setState,特别注意:不能在 shouldComponentUpdate
和 componentWillUpdate
中调用 setState
,会导致循环调用。
本篇文章到这就结束了(想看更多就到PHP中文网React使用手册栏目中学习),有问题的可以在下方留言提问。
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!