この記事では、主に react フレームワークの原理について詳しく説明します。以下では、react について詳しく説明します。今すぐこの記事を見てみましょう
。
私は React に 2 年以上取り組んできましたが、このフレームワークの利点は誰もがよく知っていますが、大規模なプロジェクトでは、その欠点が徐々に明らかになります。 >Redux 、
ReactRouter
、およびその他のサードパーティ フレームワークを使用すると、複雑なビジネス コードの量が非常に大きくなります (フロントエンド コードは通常、以前のサイズの 1.5 倍になります)。初期段階で基礎となる設計が良くないと、開発効率が低いという問題に直面することがよくあります。以下は、React フレームワークの核となる概念をいくつかまとめたものです。皆様のお役に立てれば幸いです:Redux
、ReactRouter
等三方框架后,结合复杂的业务代码量会变得非常大(前端代码常常是以前的1.5倍)。如果前期底层设计得不好,时常面临着开发效率低的问题。下面归纳了一些React框架的核心概念,希望对大家有帮助:
React diff 算法
React的diff
算法是Virtual DOM
之所以任性的最大依仗,大家知道页面的性能 一般是由渲染速度和渲染次数决定,如何最大程度地利用diff
算法进行开发?我们先看看它的原理。
传统 diff 算法
计算一棵树形结构转换成另一棵树形结构的最少操作,传统 diff 算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3)
,其中 n 是树中节点的总数。也就是说如果要展示1000个节点,就要依次执行上十亿次的比较。这个性能消耗对对于前端项目来说是不可接受的。
核心算法
如上所见,传统 diff 算法的复杂度为 O(n^3)
,显然这是无法满足性能要求的。而React
通过制定大胆的策略,将 O(n^3)
复杂度的问题转换成 O(n)
复杂度的问题。他是怎么做到的?
tree diff
Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较。如下图所示:
React 通过 updateDepth 对 Virtual DOM 树进行层级控制,只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 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(); } } } }
为什么要减少DOM节点的跨层级操作?
如下图,A 节点(包括其子节点)整个被移动到 D 节点下,由于 React 只会简单的考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作。当根节点发现子节点中 A 消失了,就会直接销毁 A;当 D 发现多了一个子节点 A,则会创建新的 A(包括子节点)作为其子节点。此时,React diff
的执行情况:create A -> create B -> create C -> delete A。
由此可发现,当出现节点跨层级移动时,并不会出现想象中的移动操作,而是以 A 为根节点的树被整个重新创建,这是一种影响 React
性能的操作。
component diff
拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
如果是同一类型的组件,按照原策略继续比较
virtual DOM tree
。如果不是,则将该组件判断为
dirty component
,从而替换整个组件下的所有子节点。-
对于同一类型的组件,有可能其
Virtual DOM
没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此React
允许用户通过shouldComponentUpdate()
React diff アルゴリズム
diff
アルゴリズムは Virtual DOM が任意である最大の理由は、ページのパフォーマンスが一般にレンダリング速度とレンダリング数によって決まることを誰もが知っているからです。 diff 開発用アルゴリズム?まずはその仕組みを見てみましょう。 <p></p>
<h2 id="従来の差分アルゴリズム">従来の差分アルゴリズム</h2>
<img src="/static/imghwm/default1.png" data-src="https://img.php.cn//upload/image/667/341/244/1536648833324341.png?x-oss-process=image/resize,p_40" class="lazy" title="1536648833324341.png" alt="React フレームワークにはどのようなアルゴリズムがありますか? Reactフレームワークのアルゴリズムを詳しく解説"> あるツリー構造を別のツリー構造に変換するために必要な最小限の操作を計算します。 従来の差分アルゴリズムはループ再帰によってノードを順次比較しますが、これは非効率であり、アルゴリズムの複雑さは O に達します。 (n^3)
、n はツリー内のノードの総数です。つまり、1,000 個のノードを表示したい場合は、数十億回の比較を順番に実行する必要があります。このようなパフォーマンスの消費は、フロントエンド プロジェクトでは許容できません。 コア アルゴリズム
🎜 上で見たように、従来の差分アルゴリズムの複雑さはO(n^3)
であり、明らかに要件を満たすことができません。パフォーマンス要件が必要です。そして React
は、大胆な戦略を立てることによって、O(n^3)
の複雑さの問題を O(n)
の複雑さの問題に変換します。彼はどうやってそんなことをしたのでしょうか? 🎜tree diff
🎜 Web UI では DOM ノードのクロスレベル移動操作はほとんどないため、無視できます。 React はツリー アルゴリズムを簡潔かつ明確に最適化しました。つまり、2 つのツリーは同じレベルのノードのみを比較します。下の図に示すように: 🎜🎜
// 卸载组件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); }
DOM ノードのクロスレベル操作を減らす必要があるのはなぜですか?
🎜以下の図に示すように、React は同じレベルのノードの位置変換のみを考慮するため、A ノード (そのサブノードを含む) は完全に D ノードに移動されます。レベルが異なる場合は、作成操作と削除操作のみが行われます。ルートノードは、A が子ノード内で消失していることを検出すると、A を直接破棄します。D は、追加の子ノード A があることを検出すると、その子ノードとして新しい A (子ノードを含む) を作成します。このとき、React diff
の実行ステータスは、create A -> create B -> create C -> delete A になります。 🎜🎜
React
パフォーマンス操作に影響します。 。 🎜component diff
🎜同じクラスを持つ 2 つのコンポーネントは同様のツリー構造を生成し、異なるクラスを持つ 2 つのコンポーネントは異なるツリー構造を生成します。 🎜- 🎜 同じタイプのコンポーネントの場合は、元の戦略に従って
仮想 DOM ツリー
の比較を続けます。 🎜🎜 - 🎜そうでない場合、コンポーネントは
ダーティ コンポーネント
と判断され、コンポーネント全体の下にあるすべての子ノードが置き換えられます。 🎜🎜 - 🎜同じタイプのコンポーネントの場合、その
Virtual DOM
に変更がない可能性があります。これを確実に把握できれば、差分操作の時間を大幅に節約できます。したがって、React
により、ユーザーはshouldComponentUpdate()
を使用して、コンポーネントの差分を取得する必要があるかどうかを判断できるようになります。 🎜🎜🎜🎜🎜🎜上の図に示すように、
コンポーネント D
がコンポーネント G
に変更されると、2 つのコンポーネント
が似た構造であっても、一度>React
> D と G が異なる種類のコンポーネントであると判断された場合、両者の構造は比較されませんが、component D
は直接削除され、component G
とその子ノードが再作成されます。React diff
は、2 つのcomponent
の型は異なるが構造が類似している場合、パフォーマンスに影響しますが、React
公式ブログに記載されているように、次のようになります。コンポーネント
がDOM ツリー
と類似する機会はほとんどないため、この極端な要因が実装および開発プロセスに重大な影響を与えることは困難です。component D
改变为component G
时,即使这两个component
结构相似,一旦React
判断 D 和 G 是不同类型的组件,就不会比较二者的结构,而是直接删除component D
,重新创建component G
以及其子节点。虽然当两个component
是不同类型但结构相似时,React diff
会影响性能,但正如React
官方博客所言:不同类型的component
是很少存在相似DOM tree
的机会,因此这种极端因素很难在实现开发过程中造成重大影响的。element diff
对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。React 提出优化策略:允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,虽然只是小小的改动,性能上却发生了翻天覆地的变化!
新老集合所包含的节点,如下图所示,新老集合进行 diff 差异化对比,通过 key 发现新老集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将老集合中节点的位置进行移动,更新为新集合中节点的位置,此时 React 给出的 diff 结果为:B、D 不做任何操作,A、C 进行移动操作,即可。
开发建议
(1)[基于tree diff] 开发组件时,保持稳定的DOM结构有助于维持整体的性能。换而言之,尽可能少地动态操作DOM结构,尤其是移动操作。当节点数过大或者页面更新次数过多时,页面卡顿的现象比较明显。可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。
(2)[基于component diff] 开发组件时,注意使用
shouldComponentUpdate()
来减少组件不必要的更新。除此之外,对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff
的性能消耗。(3)[基于element diff] 对于列表结构,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。
React Lifecycle
React的生命周期具体可分为四种情况:
当首次装载组件时,按顺序执行
getDefaultProps
、getInitialState
、componentWillMount
、render
和componentDidMount
;当卸载组件时,执行
componentWillUnmount
;当重新装载组件时,此时按顺序执行
getInitialState
、componentWillMount
、render
和componentDidMount
,但并不执行getDefaultProps
;当再次渲染组件时,组件接受到更新状态,此时按顺序执行
componentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
、render
和componentDidUpdate
。
React组件的3种状态
状态一:MOUNTING
mountComponent
负责管理生命周期中的getInitialState
、componentWillMount
、render
和componentDidMount
。状态二:RECEIVE_PROPS
updateComponent
负责管理生命周期中的componentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
、render
和componentDidUpdate
。状态三:UNMOUNTING
unmountComponent
负责管理生命周期中的componentWillUnmount
。(想看更多就到PHP中文网React参考手册栏目中学习)首先将状态设置为
UNMOUNTING
,若存在componentWillUnmount
,则执行;如果此时在componentWillUnmount
中调用setState
,是不会触发reRender
。更新状态为NULL
要素の差分
同じレベルにある子ノードのグループは、一意の ID によって区別できます。 React は最適化戦略を提案しています。開発者は、同じレベルの同じグループの子ノードを区別するために一意のキーを追加することができます。これはほんの小さな変更ですが、パフォーマンスは劇的に変化しました。 🎜🎜新旧セットに含まれるノードは下図の通りです。キーにより新旧セット内のノードが同一のノードであることが分かります。ノードを削除して作成する必要はありません。古いセット内のノードの位置が新しいセット内のノードの位置に移動および更新されるだけです。このとき、React によって得られる diff 結果は次のようになります。 :BとDは何も操作せず、AとCは移動操作を行います。 🎜🎜🎜開発の提案🎜(1)[ツリーの差分に基づく] コンポーネントを開発する場合、安定した DOM 構造を維持することは、全体的なパフォーマンスの維持に役立ちます。言い換えれば、DOM 構造の動的操作、特に移動操作はできるだけ少なくしてください。ノードの数が多すぎる場合、またはページの更新数が多すぎる場合、ページ ラグの現象が顕著になります。実際に DOM ノードを削除または追加しなくても、CSS を介してノードを非表示または表示できます。 🎜🎜(2) [コンポーネントの差分に基づく] コンポーネントを開発するときは、コンポーネントの不要な更新を減らすために
shouldComponentUpdate()
の使用に注意してください。さらに、同様の構造を可能な限りコンポーネントにカプセル化する必要があります。これにより、コードの量が削減されるだけでなく、component diff
のパフォーマンス消費も削減されます。 🎜🎜(3) [要素の差分に基づく] リスト構造の場合、ノードの数が多すぎる場合や更新操作が面倒な場合は、最後のノードをリストの先頭に移動するなどの操作を減らすようにしてください。頻度が高すぎると、React のレンダリング パフォーマンスにある程度影響します。 🎜React ライフサイクル
🎜 React のライフサイクルは 4 つの状況に分類できます: 🎜🎜🎜
- 🎜 コンポーネントが初めてロードされるときは、 order
getDefaultProps
、getInitialState
、componentWillMount
、render
、およびcomponentDidMount
🎜 > - 🎜コンポーネントをアンロードする場合は、
componentWillUnmount
を実行します 🎜 - 🎜 コンポーネントをリロードする場合は、
getInitialState
、componentWillMount、<code>render
、およびcomponentDidMount
を順序付けしますが、getDefaultProps
は実行されません 🎜 - 🎜 コンポーネントがレンダリングされるとき。再度、コンポーネントは更新されたステータスを受け取り、
componentWillReceiveProps
、ShouldComponentUpdate
、componentWillUpdate
、render
、およびを実行します。シーケンス。componentDidUpdate
。 🎜
React コンポーネントの 3 つの状態
状態 1: MOUNTING
🎜mountComponent
はgetInitialState
、componentWillMount
、render
、およびcomponentDidMount
。 🎜🎜🎜状態 2: RECEIVE_PROPS🎜
updateComponent
は、componentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
の管理を担当します。ライフサイクル >、render
、およびcomponentDidUpdate
。 🎜🎜🎜状態 3: アンマウント中🎜
unmountComponent
は、ライフサイクル内のcomponentWillUnmount
を管理します。 (さらに詳しく知りたい場合は、PHP 中国語 Web サイト React リファレンス マニュアル にアクセスして学習してください) ) 🎜🎜 まず状態をUNMOUNTING
に設定します。componentWillUnmount
が存在する場合は、componentWillUnmount
でsetState
が呼び出された場合に実行します。現時点では、reRender
はトリガーされません。更新ステータスはNULL
で、コンポーネントのアンインストール操作は完了しました。実装コードは次のとおりです: 🎜// 卸载组件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); }
React生命周期总结
生命周期 调用次数 能否使用setState() getDefaultProps 1 否 getInitialState 1 否 componentWillMount 1 是 render >=1 否 componentDidMount 1 是 componentWillReceiveProps >=0 是 shouldComponentUpdate >=0 否 componentWillUpdate >=0 否 componentDidUpdate >=0 否 componentWillUnmount 1 否 componentDidUnmount 1 否 setState实现机制
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使用手册栏目中学习),有问题的可以在下方留言提问。
以上がReact フレームワークにはどのようなアルゴリズムがありますか? Reactフレームワークのアルゴリズムを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。

CおよびJavaScriptは、WebAssemblyを介して相互運用性を実現します。 1)CコードはWebAssemblyモジュールにコンパイルされ、JavaScript環境に導入され、コンピューティングパワーが強化されます。 2)ゲーム開発では、Cは物理エンジンとグラフィックスレンダリングを処理し、JavaScriptはゲームロジックとユーザーインターフェイスを担当します。

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

SublimeText3 中国語版
中国語版、とても使いやすい

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

WebStorm Mac版
便利なJavaScript開発ツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。
