ホームページ >ウェブフロントエンド >jsチュートリアル >React コンポーネントのパフォーマンス最適化方法への回答

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

小云云
小云云オリジナル
2018-03-03 09:00:421285ブラウズ

全体的なパフォーマンスを大幅に改善しないコードにパフォーマンスの最適化エネルギーを無駄にしないでください。パフォーマンスに重要な影響を与える部分を最適化するのに早すぎるということはありません。なぜなら、パフォーマンスに影響を与える最も重要な部分は、多くの場合、ソリューションのコアに関係し、アーキテクチャ全体を決定します。将来変更が必要になった場合、アーキテクチャはさらに複雑になるからです。

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

React は、ほとんどのコンポーネントを再レンダリングするために仮想 DOM を使用しますが、以前のレンダリング コンテンツをすべて破棄するわけではありません。 Virtual DOM の助けを借りて、React は DOM ツリーへの最小限の変更を計算できます。これが React レンダリングの秘訣です

ただし、Virtual DOM は各 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算法并不复杂,首先检查两个树形的根节点的类型是否相同,根据相同或者不同有不同的处理方式:

节点类型不同的情况

如果树形节点的类型不相同,那就意味着改动很大,直接认为原来的那个树形结构已经没用,可以扔掉,需要从新构建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组件Dragact 0.1.4详解

store优化React组件的方法详解

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

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。