React コンポーネントのパフォーマンス最適化方法

小云云
リリース: 2018-03-05 09:32:29
オリジナル
1487 人が閲覧しました

1. 単一の React コンポーネントのパフォーマンスの最適化

React はレンダリング パフォーマンスを向上させるために仮想 DOM を使用しますが、以前のレンダリング コンテンツがすべて破棄されるわけではありません。 Virtual DOM、DOM の助けを借りて、React は DOM ツリーへの最小限の変更を計算できます。これが React レンダリングの秘訣です。ただし、Virtual DOM は各 DOM の量を減らすことができます。仮想 DOM の計算と比較は最小限の処理であり、依然として複雑なプロセスです


もちろん、仮想 DOM の計算を開始する前にレンダリング結果が変わらないと判断できれば、実行する必要はありません。仮想 DOM の計算と比較が高速化されます。


2. shouldComponentUpdateのデフォルト実装


レンダリング結果が変わらないと判断した場合、仮想DOMの計算を開始する前にコンポーネントのレンダリングを防ぐことができるため、パフォーマンスが向上します。当然、 shouldComponentUpdate( nextProp, nextState) を使用することを考えます


shouldComponentUpdate 関数は、「いつ再レンダリングが必要ないか」を決定するために render 関数の前に呼び出されます。

は、更新を続行するかどうかを決定するためにブール値を返します。デフォルトで true を返します。 false が返された場合、更新は中断されます。


shouldComponentUpdate(nextProp,nextState){
  return (nextProp.completed !== this.props.completed) ||
    (nextProp.text !== this.props.text)
}
ログイン後にコピー

ここで、 nextProps は、このコンポーネントのレンダリング コンテンツに影響する唯一の props です。これら 2 つのプロパティが変更されていない限り、 shouldComponentUpdate は不要な更新を防ぐために false を返すことができます


ただし、型が基本型である場合、値が同じである限り、上記の比較は単なる「浅い比較」です。が同じである場合、「浅い比較」


も 2 つが同じであると見なされます:


次に、 prop の型が複合オブジェクトの場合はどうなるでしょうか?


複雑なオブジェクトの場合、「浅い比較」メソッドは 2 つのプロパティが同じオブジェクトへの参照であるかどうかのみをチェックします。そうでない場合は、オブジェクトの内容がまったく同じであっても、それらは 2 つの異なるプロパティとみなされます。次に、「詳細比較」を使用します。ただし、オブジェクトの構造は予測できません。各フィールドに対して「詳細比較」を再帰的に実行すると、コードが複雑になるだけでなく、パフォーマンスの問題が発生する可能性があります。


したがって、前後のオブジェクトタイプのプロパティが同じであることを確認したい場合は、そのプロパティが同じJavaScriptオブジェクトを指していることを確認する必要があります:


<Foo styleProp = {{color: "red"}}>
ログイン後にコピー

上記の受信メソッドの使用を避けるにはメソッドを使用する場合、レンダリングによって {color: "red"} オブジェクトが再作成されるたびに行う必要があり、参照アドレスは毎回異なり、そのため styleProp も毎回異なります。


const footStyle = {color: "red"};//确保这个初始化只执行一次,不要放在render函数中
<Foo styleProp = {footStyle}>
ログイン後にコピー

「シングルトンモード」を使用して、渡された styleProp が同じオブジェクトを指していることを確認してください


それが関数の場合はどうなるでしょうか?


<Foo onToggle={() => onToggleTodo(item.id)}/>
ログイン後にコピー

ここで割り当てられる値は匿名関数であり、割り当て中に生成されるため、上記の関数転送モードの使用は避けるべきです。つまり、これをレンダリングするたびに新しい関数が生成されることになります。問題の場所です。


渡される小道具がたくさんある場合はどうすればよいですか?


そうですね~~React-Redux を使用している場合は、 shouldComponentUpdate のデフォルト実装があります。


3. 複数の React コンポーネントのパフォーマンスの最適化


React コンポーネントがロード、更新、アンロードされると、コンポーネントの一連のライフサイクル関数が呼び出されます。ただし、これらのライフサイクル関数は特定の React コンポーネント関数用であり、アプリケーションでは多数の React コンポーネントが上から下まで結合されており、それらの間のレンダリング プロセスはより複雑になります。


同じコンポーネントのレンダリングプロセスでも、ロードフェーズ、更新フェーズ、アンロードフェーズの 3 つのプロセスを考慮する必要があります


ロードフェーズでは、コンポーネントはとにかく一度完全にレンダリングされ、そこからすべてのサブコンポーネントがレンダリングされる必要がありますReact コンポーネントを下向きに読み込む場合は、React コンポーネントの読み込みライフ サイクルを実行する必要があるため、行うべき最適化はあまりありません。


アンインストールフェーズには、componentWillUnmount というライフサイクル関数が 1 つだけあります。この関数は、componentDidMount およびその他の仕上げ作業によって追加されたイベント処理と監視のみをクリーンアップするため、最適化の余地はありません。 React 更新フェーズ (調整) プロセスで


コンポーネント更新プロセスでは、更新された仮想 DOM が構築され、以前の仮想 DOM と比較され、相違点が特定され、最小限の DOM 操作で更新されます

調整プロセス: つまり、React の更新中に Virtual DOM の違いを見つけるプロセスは、通常、N 個のノードを持つ 2 つのツリー構造を比較するアルゴリズムです。デフォルトの比較を直接使用し、ノード数が多すぎる場合、時間計算量は O(n*3) になります。必要な操作が多すぎるため、React がこのアルゴリズムを使用することは不可能です

React で実際に使用されるアルゴリズムの時間計算量は O(N) (時間計算量はアルゴリズムに必要な命令にすぎません)最良の場合と最悪の場合) 操作順序の推定)


ReactのReconciliationアルゴリズムは複雑ではありません。 まず、2つのツリー形状のルートノードの型が同じかどうかを確認します。同じか異なるか:


異なるノードタイプの場合

如果树形节点的类型不相同,那就意味着改动很大,直接认为原来的那个树形结构已经没用,可以扔掉,需要从新构建DOM树,原有的树形上的React组件便会经历“卸载”的生命周期;

也就是说,对于Virtual DOM树这是一个“更新”过程,但是却可能引发这个树结构上某些组件的“装载”和“卸载”过程
如:

更新前


 <p>
  <Todos />
 </p>
ログイン後にコピー

我们想要更新成这样:


 <span>
   <Todos />
 </span>
ログイン後にコピー

>1. 那么在作比较的时候,一看根节点原来是p,新的是span,类型就不一样了,那么这个算法就废弃之前的p包括里面的所有子节点,从新构建一个span节点和子节点;

>2. 很明显因为根节点不同就将所有的子节点从新构建,这很浪费,但是为了避免O(N*3)的时间复杂度,React这能选择这种比较简单、快捷的方法;

>3. 所以,作为开发者,我们一定要避免上面的浪费的情景出现

节点类型相同的情况

如果两个节点类型相同时,对于DOM元素,React会保留节点对应的DOM元素,只对其节点的属性和内容做对比,然后只修改更新的部分;

节点类型相同时,对于React组件类型,React做得是根据新节点的props去更新节点的组件实例,引发组件的更新过程;

在处理完根节点对比后,React的算法会对根节点的每一个子节点重复一样的操作

多个相同子组件的情况

如果最初组件状态为:


<ul>
  <TodoItem text = "First" />
  <TodoItem text = "Second" />

</ul>
ログイン後にコピー

更新后为:


<ul>
  <TodoItem text = "First" />
  <TodoItem text = "Second" />
  <TodoItem text = "Third" />
</ul>
ログイン後にコピー

那么React会创建一个新的TodoItem组件实例,而前两个则进行正常的更新过程但是,如果更新后为:


<ul>
  <TodoItem text = "Zero" />
  <TodoItem text = "First" />
  <TodoItem text = "Second" />

</ul>
ログイン後にコピー

(这将暴露一个问题)理想处理方式是,创建一个新的TodoItem组件实例放在第一位,后两个进入自然更新过程
但是要让react按照这种方式,就必须找两个子组件的不同之处,而现有计算两个序列差异的算法时间是O(N*2),显然则
不适合对性能要求很高的场景,所以React选择了一个看起来很傻的办法,即挨个比较每个子组件;

React首先认为把text为First的组件的text改为Zero,Second的改为First,最后创建一个text为Second的组件,这样便会破原有的两个组件完成一个更新过程,并创建一个text为Second的新组件

这显然是一个浪费,React也意到,并提供了方克服,不过需要开发人员提供一点帮助,这就是key

Key的使用

key属性可以明确的告诉React每个组件的唯一标识

如果最初组件状态为:


<ul>
  <TodoItem key={1} text = "First" />
  <TodoItem key={2} text = "Second" />

</ul>
ログイン後にコピー

更新后为:


<ul>
  <TodoItem key={0} text = "Zero" />
  <TodoItem key={1} text = "First" />
  <TodoItem key={2} text = "Second" />
</ul>
ログイン後にコピー

因为有唯一标识key,React可以根据key值,知道现在的第二和第三个组件就是之前的第一和第二个,便用原来的props启动更新过程,这样shouldComponentUpdate就会发生作用,避免无谓的更新;

注意:因为作为组件的唯一标识,所以key必须唯一,且不可变

下面的代码是错误的例子:


<ul>
  todos.map((item,index) => {
      <TodoItem
        key={index}
        text={item.text}
      />
    })
</ul>
ログイン後にコピー

使用数组下标作为key值,看起来唯一,但不稳定,因为随着todos数组值的不同,同样一个组件实例在不同的更新过程中数组的下标完全可能不同,把下标当做可以就会让React乱套,记住key不仅要唯一还要确保稳定不可变

需要注意:虽然key是一个prop,但是接受key的组件不能读取key的值,因为key和ref是React保留的两个特殊prop,并没有预期让组将直接访问。

相关推荐:

关于React组件项目实践

React组件性能优化方法解答

分解React组件的几种进阶方法

以上がReact コンポーネントのパフォーマンス最適化方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!