ホームページ >ウェブフロントエンド >jsチュートリアル >Reactコンポーネントのパフォーマンス最適化に関する詳細な説明
この記事では主に React コンポーネントのパフォーマンスの最適化について紹介しますので、参考にしてください。
Gartner: 「小さなパフォーマンスの最適化を無視することを忘れるべきです。97% の場合、時期尚早な最適化が諸悪の根源であると言えます。そして、最も効果的なコードの残り 3% に注意を払う必要があります。」 「
全体的なパフォーマンスの向上がほとんどないコードにパフォーマンスの最適化エネルギーを無駄にしないでください。パフォーマンスに重要な影響を与える部分を最適化するのに早すぎるということはありません。」なぜなら、パフォーマンスに影響を与える最も重要な部分は、多くの場合、ソリューションのコアに関係し、アーキテクチャ全体を決定します。将来変更が必要になった場合、アーキテクチャはさらに複雑になるからです。
1. 単一の React コンポーネントのパフォーマンスの最適化
React はレンダリング パフォーマンスを向上させるために仮想 DOM を使用しますが、以前のレンダリング コンテンツをすべて破棄して開始するわけではありません。 Virtual DOM の助けを借りて、React は DOM ツリーへの最小限の変更を計算できます。これが React レンダリングの秘訣であり、デフォルトでは、各 DOM 操作の量を最小限に抑えることができます。仮想 DOM の計算と比較は依然として複雑なプロセスです
もちろん、仮想 DOM の計算を開始する前にレンダリング結果が変わらないと判断できれば、仮想 DOM の計算と比較を実行する必要はありません。 、速度が速くなります。
レンダリング結果が変わらないと判断した場合、仮想DOMの計算を開始する前にコンポーネントのレンダリングを防ぐことができるため、パフォーマンスが向上します。当然、 shouldComponentUpdate( nextProp, nextState) を使用することを考えます
shouldComponentUpdate 関数は、「いつ再レンダリングする必要がないのか」を判断するために render 関数の前に呼び出されます。
は、更新を続行するかどうかを判断するためにブール値を返します。デフォルトでは true を返します。 false を返した場合、更新は中断されます
このコンポーネントの場合、レンダリング コンテンツに影響する唯一の props です。これら 2 つのプロパティが変更されていない限り、 shouldComponentUpdate はそれを防ぐために false を返すことができます
ただし、型が基本型の場合、上記の比較は単なる「浅い比較」です。 、値が同じである限り、「浅い比較」
も 2 つが同じであるとみなします:
次に、 prop 型が複合型の場合はどうすればよいですか物体?
複雑なオブジェクトの場合、「浅い比較」メソッドは 2 つのプロパティが同じオブジェクトへの参照であるかどうかのみをチェックします。そうでない場合は、オブジェクトの内容がまったく同じであっても、それらは 2 つの異なるプロパティとみなされます。次に、「詳細比較」を使用します。ただし、オブジェクトの構造は予測できません。各フィールドに対して「詳細比較」を再帰的に実行すると、コードが複雑になるだけでなく、パフォーマンスの問題が発生する可能性があります。
したがって、前後のオブジェクト型の props が同じであることを確認したい場合は、props が同じ JavaScript オブジェクトを指していることを確認する必要があります:
shouldComponentUpdate(nextProp,nextState){ return (nextProp.completed !== this.props.completed) || (nextProp.text !== this.props.text) }
上記の使用を避けるには{color: "red"} オブジェクトはレンダリングが行われるたびに再作成され、参照アドレスは毎回異なり、そのため styleProp も毎回異なります。
<Foo styleProp = {{color: "red"}}>
「シングルトンモード」を使用して、渡された styleProp が同じオブジェクトを指していることを確認してください
それが関数の場合はどうなるでしょうか?
const footStyle = {color: "red"};//确保这个初始化只执行一次,不要放在render函数中 <Foo styleProp = {footStyle}>
上記の関数転送モードの使用は避けるべきです。ここで割り当てられているのは匿名関数であり、割り当て中に生成されるためです。つまり、レンダリングのたびに新しい関数が生成されます。これが問題です。にあり。
渡される小道具がたくさんある場合はどうすればよいですか?
そうですね~~React-Redux を使用している場合は、 shouldComponentUpdate のデフォルト実装があります。
React コンポーネントがロード、更新、アンロードされると、コンポーネントの一連のライフサイクル関数が呼び出されます。ただし、これらのライフサイクル関数は特定の React コンポーネント関数用であり、アプリケーションでは多数の React コンポーネントが上から下まで結合されており、それらの間のレンダリング プロセスはより複雑になります。
同じコンポーネントのレンダリングプロセスでも、ロードフェーズ、更新フェーズ、アンロードフェーズの 3 つのプロセスを考慮する必要があります
ロードフェーズでは、コンポーネントはとにかく一度完全にレンダリングされ、そこからすべてのサブコンポーネントがレンダリングされる必要がありますReact コンポーネントを下向きに読み込む場合は、React コンポーネントの読み込みライフ サイクルを実行する必要があるため、最適化を行う必要はあまりありません。
アンインストールフェーズには、componentWillUnmount というライフサイクル関数が 1 つだけあります。この関数は、componentDidMount およびその他の仕上げ作業によって追加されたイベント処理と監視のみをクリーンアップするため、最適化の余地はありません。 React 更新フェーズ (調整) プロセスで
コンポーネント更新プロセス中、更新された仮想 DOM が構築され、以前の仮想 DOM と比較され、違いが確認され、最小限の DOM 操作で更新されます
调和过程:即React更新中对Virtual DOM找不同的过程,通常对比两个N个节点的树形结构的算法,时间复杂度是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,并没有预期让组将直接访问。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Vue で Compass を使用する具体的な方法はありますか?
vue2.0のスワイパーコンポーネントを使用してカルーセルを実装します(詳細なチュートリアル)
以上がReactコンポーネントのパフォーマンス最適化に関する詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。