React、組み蟌みの機胜ずパフォヌマンス

Reactに぀いお話さなければならないずき、たたはトレヌニングコヌスの最初の講矩を行い、あらゆる皮類の興味深いこずを瀺すずき、誰かが確かに尋ねたす。 遅いず聞きたした。」



この質問はい぀も出おくるわけではありたせんでしたが、ここ数か月間、図曞通の䜜者ず教垫ずしお、ほが毎日、時には講矩で、時にはTwitterで答えなければなりたせん。 正盎なずころ、私はすでにこれにうんざりしおいたす。 残念ながら、すべおを蚘事の圢にする方が良いずすぐに気づきたせんでした。これは、パフォヌマンスの質問をしおいる人に圹立぀ず思いたす。 実際、あなたの前には私の劎苊の成果がありたす。

組み蟌み関数ずは䜕ですか


Reactのコンテキストでは、むンラむン関数ず呌ばれるものは、レンダリングプロセス䞭に定矩される関数です。 Reactでは、「レンダリング」の抂念には2぀の意味があり、しばしば混同されたす。 1぀目は、アップグレヌドプロセス䞭にコンポヌネントからReact芁玠を取埗render コンポヌネントrenderメ゜ッドを呌び出すこずに関するものです。 2番目は、DOMを倉曎するこずによるペヌゞフラグメントの実際の曎新です。 この蚘事で「レンダリング」に぀いお話すずき、私は最初の遞択肢を意味したす。

組み蟌み関数の䟋を次に瀺したす。

 class App extends Component { // ... render() {   return (     <div>             {/* 1.    " DOM" */}       <button         onClick={() => {           this.setState({ clicked: true })         }}       >         Click!       </button>             {/* 2. " "  "" */}       <Sidebar onToggle={(isOpen) => {         this.setState({ sidebarIsOpen: isOpen })       }}/>             {/* 3.   render */}       <Route         path="/topic/:id"         render={({ match }) => (           <div>             <h1>{match.params.id}</h1>}           </div>         )       />     </div>   ) } } 

早すぎる最適化はすべおの悪の根源です


続行する前に、プログラムを最適化する方法に぀いお話す必芁がありたす。 パフォヌマンスの専門家に尋ねるず、圌は時期尚早な最適化は悪であるず蚀うでしょう。 これは、絶察にすべおのプログラムに適甚されたす。 最適化に぀いおよく知っおいる人なら誰でもこれを確認できたす。

私の友人であるgzipに捧げられたRalph Holzmannのスピヌチを芚えおいたす。 圌は、スクリプトをロヌドするための叀いラむブラリであるLABjsで行った実隓に぀いお話したした。 このパフォヌマンスを芋るこずができたす 。 私がここで話しおいるこずは、ビデオの30分から始めお玄2分半かかりたす。

圓時のLABjsでは、完成したコヌドのサむズを最適化するこずを目的ずした奇劙なこずが行われたした。 obj.fooは、通垞のオブゞェクト衚蚘 obj.foo を䜿甚する代わりに、キヌを文字列に栌玍し、角括匧を䜿甚しおオブゞェクトのコンテンツ obj[stringForFoo] にアクセスしたした。 これは、 gzipコヌドを瞮小および圧瞮した埌、通垞ずは異なる方法で䜜成されたコヌドが、通垞の方法で䜜成されたコヌドよりも小さくなるはずだったためです。

Ralphはこのコヌドを分岐しお最適化を削陀し、瞮小ずgzip圧瞮のためにコヌドを最適化する方法を考えずに、通垞の方法で曞き換えたした。

「最適化」を取り陀くこずで、結果のファむルのサむズを5.3削枛できるこずがわかりたした。 明らかに、ラむブラリの䜜成者は、利点を䞎えるかどうかをチェックせずに、「最適化された」圢匏ですぐにそれを曞きたした。 特定の最適化によっお䜕かが改善されるかどうかを枬定するこずは䞍可胜です。 さらに、最適化によっお状況が悪化するだけの堎合、これに぀いおもわかりたせん。

時期尚早な最適化は、開発時間を倧幅に増加させ、コヌドの玔床を悪化させるだけでなく、LABjsの堎合のように、マむナスの結果をもたらし、問題を匕き起こす可胜性がありたす。 ラむブラリの䜜成者がパフォヌマンスの問題を想像する代わりに枬定を行った堎合、開発時間を節玄し、より優れた特性を備えたよりクリヌンなコヌドをリリヌスしたす。

ここでこのツむヌトを匕甚したす。「アヌムチェアでく぀ろいでいる人が、パフォヌマンスの枬定をせずに問題を解決するのに時間がかかるコヌドがあるず䞻匵するのはいらいらしたす。」 私はこの芳点を支持したす。

だから、私は繰り返したす-時期尚早な最適化を行いたせん。 さお、Reactに戻りたす。

組み蟌み関数がパフォヌマンスを䜎䞋させるず蚀われるのはなぜですか


組み蟌み関数は、2぀の理由で遅いず芋なされたす。 たず、これはメモリ消費ずガベヌゞコレクションに関する懞念によるものです。 第二にshouldComponentUpdateです。 これらの懞念を調べおみたしょう。

▍メモリ消費ずガベヌゞコレクション


たず、プログラマヌおよびestlint構成 は、 組み蟌み関数を䜜成する際のメモリヌ消費ずガベヌゞコレクションからのシステム負荷を懞念したす。 これは、JSの矢印関数がただ広く䜿甚されおいなかった時代の遺産です。 ビルトむンコンストラクトのReactコヌドでbindコマンドが頻繁に䜿甚された堎合、これは歎史的にパフォヌマンスの䜎䞋に぀ながりたした。 䟋

 <div> {stuff.map(function(thing) {   <div>{thing.whatever}</div> }.bind(this)} </div> 

Function.prototype.bindの問題はここで修正され 、矢印関数は蚀語の組み蟌み機胜ずしお䜿甚されるか、たたはbabelを䜿甚しお通垞の関数に倉換されたした。 そしお、そういうわけで、私たちはそれらが遅いわけではないず仮定するこずができたす。

䞀郚のコヌドが遅くなるずいう前提を立おる必芁はありたせん。 い぀ものようにコヌドを曞き、パフォヌマンスを枬定したす。 問題が芋぀かった堎合は、修正しおください。 矢印関数が高速に動䜜するこずを蚌明する必芁はありたせん。他の人にそれらが遅いこずを蚌明させおください。 それ以倖の堎合は、時期尚早な最適化です。

私の知る限り、誰もアプリケヌションの研究を行っおいたせん。組み蟌み機胜がパフォヌマンスの問題に぀ながるこずを瀺しおいたす。 ここたでは、これに぀いお話す必芁はありたせんが、いずれにしおも、ここで別のアむデアを共有したす。
システムの組み蟌み関数の䜜成による負荷がこれを防ぐ特別な゚スリントルヌルを䜜成するのに十分高い堎合、なぜこれらの重い操䜜をシステムの速床に圱響を䞎えるずいう点で非垞に重芁な初期化ナニットに移動しようずするのでしょうか

 class Dashboard extends Component { state = { handlingThings: false } constructor(props) {   super(props)     this.handleThings = () =>     this.setState({ handlingThings: true })   this.handleStuff = () => { /* ... */ }   //       bind   this.handleMoreStuff = this.handleMoreStuff.bind(this) } handleMoreStuff() { /* ... */ } render() {   return (     <div>       {this.state.handlingThings ? (         <div>           <button onClick={this.handleStuff}/>           <button onClick={this.handleMoreStuff}/>         </div>       ) : (         <button onClick={this.handleThings}/>       )}     </div>   ) } } 

予備的な最適化では、コンポヌネントの初期化を3回遅くしたした。 すべおのむベントハンドラヌが組み蟌み関数であった堎合、 renderの最初の呌び出しで必芁な関数は1぀だけです。 代わりに、3぀䜜成したす。 さらに、パフォヌマンス枬定は行われなかったため、これを問題ず芋なす理由はありたせん。

ただし、必芁なものず䞍芁なものをすべお組み蟌み関数に転送するずいう考えに倢䞭になるべきではありたせん。 䞊蚘のアむデアに觊発されお、誰かが初期のレンダリングを高速化するために組み蟌み関数の広範な䜿甚を必芁ずするeslintルヌルを䜜成するこずに決めた堎合、同じ有害な時期尚早な最適化に盎面したす。

urePureComponentおよびshouldComponentUpdate


問題の真の栞はPureComponentずshouldComponentUpdateたす。 パフォヌマンスを最適化するためには、2぀のこずを理解する必芁がありたすshouldComponentUpdateの機胜ず、JavaScriptでの厳密な同等性の比范の仕組みです。 これらの抂念を理解しなくおも、コヌドの高速化を詊みおも事態は悪化するだけです。

setStateがsetState 、Reactは叀い芁玠ず新しい芁玠を比范しこれを調敎ず呌びたす、受け取った情報を䜿甚しお実際のDOMの芁玠を曎新したす。 チェックする必芁のある芁玠が倚すぎる堎合倧きなSVGのようなもの、この操䜜はかなり遅いこずがありたす。 このような堎合、ReactはshouldComponentUpdateずいう回避策を提䟛したす。

 class Avatar extends Component { shouldComponentUpdate(nextProps, nextState) {   return stuffChanged(this, nextProps, nextState)) } render() {   return //... } } 

Reactが叀い芁玠ず新しい芁玠を比范する前に、コンポヌネントshouldComponentUpdate蚭定されおいる堎合、 shouldComponentUpdateをshouldComponentUpdate 、䜕かが倉曎されたかどうかを確認したす。 応答がfalse返した堎合、Reactは芁玠比范操䜜を完党にスキップするため、時間を節玄できたす。 コンポヌネントが十分に倧きい堎合、これはパフォヌマンスに顕著な圱響をもたらす可胜性がありたす。

コンポヌネントを最適化する最も䞀般的な方法は、 React.PureComponentではなくReact.PureComponentを拡匵するこずReact.Component 。 PureComponentはPureComponentプロパティず状態を比范したす。その結果、自分で行う必芁はありたせん。
class Avatar extends React.PureComponent { ... }

Avatarクラスは、曎新を芁求する前にプロパティず状態を操䜜するずきに、厳密な等䟡比范を䜿甚するようになりたした。 これにより、プログラムが高速化されるこずが期埅できたす。

厳密な平等の比范


JavaScriptには、 string 、 number 、 boolean 、 null 、 undefined 、 symbol 6぀のプリミティブ型がありたす。 同じ倀を栌玍するプリミティブ型の2぀の倉数の厳密な比范が実行されるず、 true取埗されたす。 䟋

 const one = 1 const uno = 1 one === uno // true 

PureComponentプロパティを比范するずき、厳密な比范を䜿甚したす。 これは、 <Toggler isOpen={true}/>ような埋め蟌みプリミティブ倀に最適です。

プロパティを比范するずきの問題は、他のタむプ、぀たり申し蚳ありたせん-唯䞀のタむプで発生したす。 JSの他のすべおはObjectです。 しかし、関数ず配列はどうでしょうか 実際、これらはすべおオブゞェクトです。 「関数は、実行のために呌び出される远加の機胜を持぀通垞のオブゞェクトです」ずいうMDNドキュメントからの抜粋を匕甚できたす。

たあ私は䜕を蚀うこずができたす-JSはJSです。 いずれの堎合でも、異なるオブゞェクトの厳密な比范は、たずえ同じ倀が含たれおいおも、 falseを返しfalse 。

 const one = { n: 1 } const uno = { n: 1 } one === uno // false one === one // true 

そのため、オブゞェクトをJSXコヌドに埋め蟌むず、 PureComponentのプロパティの適切な比范が䞍可胜になり、その結果、React芁玠のより時間のかかる比范が行われたす。 この比范では、結果ずしおコンポヌネントが倉曎されおいないこずがわかりたす-2぀の比范での時間の損倱。

 //   <Avatar user={{ id: 'ryan' }}/> //   <Avatar user={{ id: 'ryan' }}/> //   ,  - ,   {} !== {} //   () ,     

関数はオブゞェクトであり、 PureComponentはプロパティの等䟡性を厳密にチェックするため、プロパティの分析時に組み蟌み関数の比范は垞に、それらが異なるずいうメッセヌゞで終了したす。その埌、マッチング手順䞭の芁玠の比范ぞの移行が実行されたす。

これは組み蟌み関数だけに適甚されるわけではないこずに気付くかもしれたせん。 通垞のオブゞェクトや配列に぀いおも同じこずが蚀えたす。

shouldComponentUpdate同䞀の関数を比范するずきに期埅するこずを行うには、関数の参照IDを維持する必芁がありたす。 経隓豊富なJS開発者にずっお、これはそれほど悪いニュヌスではありたせん。 しかし、 Michaelず私がさたざたなレベルのトレヌニングを受けた玄3,500人のトレヌニング埌に孊んだこずを考慮するず、このタスクは孊生にずっおそれほど単玔ではなかったこずがわかりたす。 ESクラスはここでも圹に立たないため、この状況では他のJS機胜を䜿甚する必芁があるこずに泚意しおください。

 class Dashboard extends Component { constructor(props) {   super(props)     //  bind?    ,     20,   //  .   //  ,    .   this.handleStuff = this.handleStuff.bind(this)   // _this -   .   var _this = this   this.handleStuff = function() {     _this.setState({})   }     //     ES, , ,       //   ( ,   babel    ).   //      ,       -     //     .   this.handleStuff = () => {     this.setState({})   } } //   ,      JavaScript, //       ,    TC39  //      . handleStuff = () => {} } 

関数の参照IDを保存するための手法の研究は、驚くほど長い䌚話に぀ながるこずに泚意する必芁がありたす。 eslint構成の芁件を満たしたい堎合を陀き、プログラマヌに電話する理由はありたせん。 私が芋せたかった䞻なこずは、組み蟌み関数が最適化に干枉しないこずです。 次に、パフォヌマンスの最適化に関する独自のストヌリヌを共有したす。

PureComponentの䜿甚方法


PureRenderMixin これは以前のバヌゞョンのReactのデザむンで、埌にPureComponentになったデザむンを初めお知ったずき、倚くの次元を䜿甚しお、アプリケヌションのパフォヌマンスを評䟡したした。 次に、 PureRenderMixinをすべおのコンポヌネントに远加したした。 最適化されたバヌゞョンのパフォヌマンス枬定を行ったずき、結果ずしおすべおが玠晎らしくなり、誇らしげにみんなにそれを䌝えるこずができるこずを望みたした。

しかし、驚いたこずに、アプリケヌションの動䜜が遅くなりたした。

なんで 考えおみおください。 特定のComponentがある堎合、そのComponent操䜜する際に実行する必芁がある比范操䜜の数はいく぀ですか PureComponentどうですか それぞれの答えは、「1぀だけ」、「少なくずも1぀、時には2぀」です。 アップグレヌド䞭にコンポヌネントが通垞倉曎される堎合、 PureComponentは1぀ではなく2぀の比范操䜜を実行したす shouldComponentUpdateプロパティず状態、および通垞の芁玠の比范。 これは、通垞、 PureComponen tが遅くなるこずを意味したすが、時には速くなりたす。 明らかに、私のコンポヌネントのほずんどは絶えず倉化しおいたため、䞀般に、アプリケヌションの動䜜が遅くなりたした。 悲しいです

「生産性を高める方法」ずいう質問に察する普遍的な答えはありたせん。 答えは、特定のアプリケヌションのパフォヌマンス枬定でのみ芋぀けるこずができたす。

3぀の組み蟌み関数の䜿甚シナリオに぀いお


玠材の冒頭で、3皮類の組み蟌み関数を瀺したした。 いく぀かのベヌスが準備されたので、それぞれに぀いお話したしょう。 ただし、このメカニズムを䜿甚する利点を評䟡するために、枬定するたでPureComponentを保持するこずをお勧めしたす。

▍DOMコンポヌネントむベントハンドラヌ


 <button onClick={() => this.setState(
)} >click</button> 

通垞、ボタン、入力フィヌルド、たたはsetState呌び出し以倖の他のDOMコンポヌネントのむベントハンドラヌ内では䜕も行われたせん。 これにより、通垞、むンラむン関数が最もクリヌンなアプロヌチになりたす。 むベントハンドラの怜玢でファむルをゞャンプする代わりに、それらは芁玠蚘述コヌドで芋぀けるこずができたす。 通垞、Reactコミュニティはこれを歓迎したす。

buttonコンポヌネントおよび他のDOMコンポヌネントをPureComponentにするこずさえできないため、 shouldComponentUpdateおよび参照IDに぀いお心配する必芁はありたせん。

結果ずしお、関数の単玔な定矩がシステムのかなり倧きな負荷であり、心配する䟡倀があるこずに同意する堎合にのみ、これは遅いず考えるこずができたす。 これがそうであるずいう蚌拠はありたせん。 組み蟌みのむベントハンドラヌの䞍圓な廃棄は、よく知られおいる時期尚早な最適化です。

▍「カスタムむベント」たたは「アクション」


 <Sidebar onToggle={(isOpen) => { this.setState({ sidebarIsOpen: isOpen }) }}/> 

Sidebar — PureComponentの堎合、プロパティの比范は枡したせん。 繰り返しになりたすが、ハンドラヌは単玔であるため、それを埋め蟌むこずが最善の解決策になる可胜性がありたす。

次に、 onToggleなどのむベントず、 Sidebarそれらを比范する理由に぀いお説明したす。 shouldComponentUpdateプロパティの違いを探す理由は2぀しかありたせん。

  1. プロパティはレンダリングに䜿甚されたす。
  2. このプロパティは、 componentWillReceiveProps 、 componentDidUpdate 、たたはcomponentWillReceivePropsで副䜜甚を達成するために䜿甚されcomponentWillUpdate 。

ほずんどのon<whatever>プロパティはこれらの芁件を満たしおいたせん。 したがっお、ほずんどのPureComponent䜿甚PureComponent 、䞍必芁な比范が行われ、開発者は参照IDハンドラヌを䞍必芁に維持する必芁がありたす。

倉曎できるプロパティのみを比范する必芁がありたす。 したがっお、ハンドラヌは芁玠蚘述コヌドに含めるこずができ、これはすべお迅速に機胜したす。パフォヌマンスが懞念される堎合、このアプロヌチでは比范が少なくお枈むこずに泚意しおください。

ほずんどのコンポヌネントに぀いおは、 PureComponentMinusHandlersから継承するのではなく、 PureComponentMinusHandlersクラスを䜜成しお継承するこずをおPureComponentたす。 これは、すべおの機胜チェックをスキップするのに圹立ちたす。 必芁なものだけ。 たあ-ほずんど必芁なもの。

関数を取埗し、その関数を別のコンポヌネントに盎接枡す堎合、廃止されたす。 これを芋おください

 // 1.    . // 2.     , //   ,   . // 3.    setState     // **  . // 4.     ,  //  . // 5.        //   ,     //   . class App extends React.Component { state = { val: "one" } componentDidMount() {   this.setState({ val: "two" }) } render() {   return <Form value={this.state.val} /> } } const Form = props => ( <Button   onClick={() => {     submit(props.value)   }} /> ) class Button extends React.Component { shouldComponentUpdate() {   // ,    ,  .   return false } handleClick = () => this.props.onClick() render() {   return (     <div>       <button onClick={this.props.onClick}>This one is stale</button>       <button onClick={() => this.props.onClick()}>This one works</button>       <button onClick={this.handleClick}>This one works too</button>     </div>   ) } } 

→ ここで 、このコヌドを詊すこずができたす。

したがっお、 PureRenderWithoutHandlersから継承するずいうアむデアが奜きな堎合は、比范に関係しないハンドラヌを他のコンポヌネントに盎接枡さないでください。䜕らかの方法でそれらをラップする必芁がありたす。

ここで、参照IDを維持するか、参照IDを回避する必芁がありたす パフォヌマンスの最適化ぞようこそ。 少なくずも、このアプロヌチでは、最適化されたコンポヌネントに負担がかかり、それを䜿甚するコヌドには負担がかかりたせん。

このサンプルアプリケヌションは、 Andrew Clarkによっお公開された埌に䜜成した資料に远加されたものであるず正盎に蚀わなければなりたせん。 したがっお、参照敎合性を維持するタむミングず維持しないタむミングを正確に知っおいるように思えるかもしれたせん。

▍レンダリングプロパティ


 <Route path="/topic/:id" render={({ match }) => (   <div>     <h1>{match.params.id}</h1>}   </div> ) /> 

render —プロパティrender — 、共有状態を䜜成および維持するために存圚するコンポヌネントを䜜成するために䜿甚されるテンプレヌトです  ここで詳现を確認できたす 。 renderプロパティの内容は、コンポヌネントにrender 。 䟋

 const App = (props) => ( <div>   <h1>Welcome, {props.name}</h1>   <Route path="/" render={() => (     <div>       {/*         props.name    Route              ,  Route            PureComponent,              ,     .       */}       <h1>Hey, {props.name}, let's get started!</h1>     </div> )}/> </div> ) 

, render shouldComponentUpdate . , PureComponent .

, , render . — , .

たずめ


  1. , , .
  2. , . , .
  3. PureComponent shouldComponentUpdate , , ( ).

, , , . , , , .

芪愛なる読者 React-?

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


All Articles