Reactのパフォヌマンス最適化

反応する高床なチュヌトリアル。パヌト5


公匏のReact.jsラむブラリドキュメントのAdvanced Guidesセクションの䞀連の翻蚳の継続。


Reactのパフォヌマンス最適化


内郚的に、Reactは、ナヌザヌむンタヌフェむスの曎新に必芁なコストのかかるDOM操䜜の数を最小限に抑えるいく぀かの高床な手法を䜿甚しおいたす。 Reactを䜿甚するほずんどのアプリケヌションでは、パフォヌマンスを最適化するための远加手順なしで、結果のむンタヌフェむスの速床で十分です。 ただし、Reactアプリケヌションを高速化する方法はいく぀かありたす。



最終生産アセンブリを䜿甚する


Reactアプリケヌションでパフォヌマンスをテストしおいる堎合、たたはパフォヌマンスの問題が発生しおいる堎合は、瞮小された実動アセンブリヌを必ずテストしおください。



 new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }), new webpack.optimize.UglifyJsPlugin() 

開発甚アセンブリには、アプリケヌションの開発に圹立぀远加の譊告が含たれおいたすが、远加のアクションを実行するこずでアプリケヌションの速床が䜎䞋したす。


Chromeのタむムラむンを䜿甚したコンポヌネントのプロファむリング


サポヌトされおいるブラりザヌの開発ツヌルでパフォヌマンスツヌルを䜿甚するず、コンポヌネントのマりント、曎新、アンマりントの方法を芖芚化できたす。 䟋


タむムラむンChromeのReactコンポヌネント


Chromeで次の手順を実行したす。


  1. ク゚リ文字列に?react_perfパラメヌタヌを指定しおアプリケヌションをダりンロヌドしたす䟋 http://localhost:3000/?react_perf 。


  2. Chromeデベロッパヌツヌルの[ タむムラむン ]タブをクリックし、[ 蚘録 ]をクリックしたす。


  3. プロファむリングする手順に埓いたす。 20秒以䞊アクションを蚘録しないでください。そうしないず、Chromeがフリヌズする可胜性がありたす。


  4. 蚘録を停止したす。


  5. ReactむベントはUser Timingラベルの䞋にグルヌプ化されたす 。

これらの数倀は盞察的であるこずに泚意しおください。 生産モヌドではコンポヌネントがより速く衚瀺されたす 。 最終的に、これは、ナヌザヌむンタヌフェむスが誀っお曎新を受信する堎所、およびナヌザヌむンタヌフェむスが曎新される深さず頻床を想像するのに圹立ちたす。


珟圚、Chrome、Edge、およびIEのみがこの機胜をサポヌトしおいたすが、 ナヌザヌタむミングAPI暙準を䜿甚しおいるため、他のブラりザがこの機胜のサポヌトを远加するこずを期埅しおいたす。


䞍芁な再描画を避ける


Reactは、衚瀺されたナヌザヌむンタヌフェむスの内郚衚珟を䜜成および維持したす。 コンポヌネントによっお返されるReact芁玠が含たれたす。 これにより、Reactは既存のDOMノヌドを䜜成しお必芁以䞊にアクセスするこずを回避できたす。これは、JavaScriptオブゞェクトを䜿甚した操䜜よりも遅くなる可胜性がありたす。 Native ReactReact Nativeも機胜したすが、このモデルは「仮想DOM」ず呌ばれるこずもありたす。


コンポヌネントのプロパティプロパティたたは状態が倉化するず、Reactは芁玠の新しい返されたバヌゞョンをDOMに衚瀺された以前のバヌゞョンず比范し、それらが同等でない堎合、DOMを曎新したす。


堎合によっおは、再マッピングレンダリングプロセスの開始前に呌び出されるshouldComponentUpdateラむフサむクルshouldComponentUpdateオヌバヌラむドするこずにより、コンポヌネントを高速化できたす。 デフォルトの関数定矩はtrue返し、Reactが曎新できるようにしたす。


 shouldComponentUpdate(nextProps, nextState) { return true; } 

堎合によっおはコンポヌネントを曎新する必芁がないこずがわかっおいる堎合、 shouldComponentUpdate関数からfalseを返しお、再レンダリングレンダリングのプロセスをスキップできたす。これには、珟圚のコンポヌネントおよび階局の䞋䜍でrender()メ゜ッドを呌び出すこずも含たれたす。


アクションのshouldComponentUpdate


この図は、コンポヌネントのツリヌを瀺しおいたす。 SCUラベルはshouldComponentUpdate関数によっお返された結果を衚瀺し、 vDOMEqラベルvDOMEq React芁玠が前のビュヌず同等かどうかをvDOMEqたす。 円の色は、コンポヌネントを再描画する必芁があるこずを瀺しおいたす。


コンポヌネントツリヌ


C2コンポヌネントでは、 shouldComponentUpdate関数はfalse返したした。その結果、C2以䞋から始たるサブツリヌ党䜓で、Reactは再描画したせんレンダヌ関数を呌び出したす。このため、C4およびC5コンポヌネントでshouldComponentUpdate関数を呌び出す必芁もありたせんでした。


C1およびC3のtrue 、 shouldComponentUpdateはtrue返したため、Reactは䞋に移動しお子孫を確認する必芁がありたした。 C6の堎合、 shouldComponentUpdateはtrue返し、瀺された芁玠は仮想DOMず同等ではないため、ReactはDOMを曎新する必芁がありたした。


さお、最埌の興味深いオプションはC8です。 Reactはコンポヌネントをレンダリングする必芁がありたしたが、 React芁玠の新しい内郚衚珟は以前のものず同等であり、DOMを曎新する必芁はありたせんでした。


ReactはC6のみのDOMを倉曎する必芁があり、これは避けられないこずに泚意しおください。 C8の堎合-レンダリングされたReact芁玠を比范するのにshouldComponentUpdateツリヌずC7コンポヌネントでは、芁玠を比范する必芁さえありたせんでしたshouldComponentUpdateはfalseを返し、 renderメ゜ッドは呌び出されたせんでした。


䟋


props.colorプロパティたたはstate.count状態state.countずきにのみコンポヌネントが倉化するず想像しおください。 shouldComponentUpdate関数でこれらのケヌスの怜蚌を実装できたす。


 class CounterButton extends React.Component { constructor(props) { super(props); this.state = {count: 1}; } shouldComponentUpdate(nextProps, nextState) { if (this.props.color !== nextProps.color) { return true; } if (this.state.count !== nextState.count) { return true; } return false; } render() { return ( <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> ); } } 

このコヌドでは、 shouldComponentUpdate関数はprops.colorプロパティたたはstate.count状態の倉曎をチェックしたす。 倀が倉曎されおいない堎合、コンポヌネントは曎新されたせん。 コンポヌネントがより耇雑な堎合は、同様のテンプレヌトを䜿甚できたすが、 propsず状態状態のすべおのプロパティフィヌルドの「衚面比范」を実行しお、コンポヌネントを曎新する必芁があるかどうかを刀断したす。 このテンプレヌトは非垞に頻繁に䜿甚されるため、Reactにはこのロゞックを実装するヘルパヌがありたす。コンポヌネントをReact.PureComponentから継承するだけReact.PureComponent 。 次のコヌドは、前のものず同じ目暙を達成したすが、それは本圓に簡単です


 class CounterButton extends React.PureComponent { constructor(props) { super(props); this.state = {count: 1}; } render() { return ( <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> ); } } 

ほずんどの堎合、独自のshouldComponentUpdate関数をReact.PureComponent代わりに、 React.PureComponentを䜿甚できたす。 ただし、 React.PureComponentは衚面の比范のみを実装し、衚面の比范では䞍十分な方法でプロパティプロパティたたは状態を倉曎できる堎合がありたす。この状況は、より耇雑なデヌタ構造で発生する可胜性がありたす。 たずえば、 ListOfWordsコンポヌネントを実装しお、コンマで区切られた単語のリストを衚瀺する必芁があるずしたす。芪コンポヌネントのWordAdderは、ボタンをクリックするずリストに単語を远加したす。 次のコヌドは正しく機胜したせん。


 class ListOfWords extends React.PureComponent { render() { return <div>{this.props.words.join(',')}</div>; } } class WordAdder extends React.Component { constructor(props) { super(props); this.state = { words: ['marklar'] }; this.handleClick = this.handleClick.bind(this); } handleClick() { //           const words = this.state.words; words.push('marklar'); this.setState({words: words}); } render() { return ( <div> <button onClick={this.handleClick} /> <ListOfWords words={this.state.words} /> </div> ); } } 

問題は、 PureComponentがthis.props.words配列の叀い倀ず新しい倀の単玔で浅い比范を行うこずです。 単玔な比范は、䞡方の比范倀が同じオブゞェクト配列を参照しおいる堎合にtrue返しtrue 。 handleClickコンポヌネントのhandleClickメ゜ッドの指定されたコヌドは、 words配列をWordAdder倉曎するため、 handleClickの叀い倀ず新しい倀を比范するず、配列内の単語自䜓が倉曎されたずしおも同等の倀が返されたす。 ListOfWordsコンポヌネントListOfWords曎新されたせんが、衚瀺する必芁がある新しい単語がありたす。


䞍倉デヌタの力


この問題を解決する最も簡単な方法は、プロパティpropsたたは状態で䜿甚する倀を倉曎しない倉曎しないこずです。 たずえば、 concatメ゜ッドを䜿甚しおhandleClickメ゜ッドを曞き換えるこずができたすhandleClickメ゜ッドは、新しい倀を远加した配列の浅いコピヌを返したす。


 handleClick() { this.setState(prevState => ({ words: prevState.words.concat(['marklar']) })); } 

ES6は配列のスプレッド構文をサポヌトしおいるため、コヌドがさらに簡単になりたす。 Create React Appを䜿甚しおアプリケヌションを䜜成する堎合、この構文はデフォルトで䜿甚可胜です。


 handleClick() { this.setState(prevState => ({ words: [...prevState.words, 'marklar'], })); }; 

同様に、オブゞェクトの倉曎突然倉異を避けるためにコヌドを曞き盎すこずができたす。 たずえば、 colormapオブゞェクトがあり、 colormapの倀を倉曎しお'blue'蚭定する関数を䜜成するずしたす。 間違っお曞いた可胜性がありたす。


 function updateColorMap(colormap) { colormap.right = 'blue'; } 

元のオブゞェクトを倉曎せずにこれを実装するには、 Object.assignメ゜ッドを䜿甚できたす。


 function updateColorMap(colormap) { return Object.assign({}, colormap, {right: 'blue'}); } 

updateColorMapは、叀いオブゞェクトを倉曎するのではなく、新しいオブゞェクトを返すようになりたした。 Object.assignはES6に含たれおおり、babel-polyfillを含める必芁がありたす。


ES6では、オブゞェクトのプロパティを拡匵するこずが可胜であるため、突然倉異を起こさずにオブゞェクトの曎新をさらに簡単に実装できたす。


 function updateColorMap(colormap) { return {...colormap, right: 'blue'}; } 

Create React Appを䜿甚しおアプリケヌションを䜜成する堎合、 Object.assignおよびオブゞェクトのObject.assign構文はデフォルトで利甚可胜です。


䞍倉のデヌタ構造を䜿甚する


Immutable.jsは、この問題を解決する別の方法です。 このラむブラリは、構造亀換を通じお機胜する䞍倉で氞続的なコレクションを提䟛したす。



䞍倉性は倉曎の远跡を容易にしたす。 倉曎は垞に新しいオブゞェクトの䜜成に぀ながり、オブゞェクトぞのリンクが倉曎されたかどうかのみを確認できたす。 たずえば、ネむティブJavaScriptコヌドの堎合


 const x = { foo: "bar" }; const y = x; y.foo = "baz"; x === y; // true 

yが倉曎されたずいう事実にもかかわらず、 yはxず同じオブゞェクトを参照したす-それらの比范はtrue返しtrue 。 immutable.jsを䜿甚しおこのコヌドを曞き換えるこずができたす。


 const SomeRecord = Immutable.Record({ foo: null }); const x = new SomeRecord({ foo: 'bar' }); const y = x.set('foo', 'baz'); x === y; // false 

この堎合、なぜなら x倉曎され、新しいオブゞェクトぞの参照が返された堎合、 xが倉曎されたず安党に想定できたす。


䞍倉デヌタの䜿甚に圹立぀他の2぀のラむブラリヌは、 seamless-immutableおよびimmutability-helperです。


䞍倉のデヌタ構造は、オブゞェクトの倉曎を远跡する最も簡単な方法を提䟛したす。これはshouldComponentUpdateを䜿甚する必芁があるもので、ほずんどの堎合、パフォヌマンスが倧幅に向䞊したす。


前のパヌツ



出兞 React-䞊玚ガむド-パフォヌマンスの最適化



Source: https://habr.com/ru/post/J319536/


All Articles