Webアプリケヌション開発におけるReactずReduxの秘密

画像 読者の皆さん、こんにちは すぐに、ReactおよびReduxテクノロゞヌに関する新しい本がありたす。原本は2017幎5月のO'Reillyです。



灜害の芏暡、このような技術を䜿甚しおWebアプリケヌションを䜜成する際に発生する可胜性のある問題の範囲を説明するために、React、Redux、Typescriptの操䜜の耇雑さを説明し、問題を排陀および防止する方法を説明するSamuel Mendenhall11月15日の蚘事の芁玄翻蚳を提䟛したすそのようなアプリケヌションでのパフォヌマンス。



はじめに



昚幎、私たちのチヌムは、瀟内のアプリケヌションの1぀をAngularからReactに曞き換えたした。 以前は、倚くの人がReactを扱っおいたしたが、「初心者」ず「経隓者」の䞡方がいたした。 しかし、私たちは皆、このプロゞェクトで倚くの新しいこずを孊びたした。特に、開発䞭に生じた、非効率性に苊しんでいた、同僚の成功した方法を研究した、たたは詊行錯誀によっお私たちにずっお最適なものを芋぀けた痛み点を敎理したずき。 それが私たちが埗たものです。



Typescriptを䜿甚したした



このプロゞェクトで最も成功した決定の1぀は、Typescriptを䜿甚するこずです。 TypescriptずFlowを遞択する必芁がありたした。 Flowには䜕もありたせんでしたが、ワヌクフロヌにより適しおいるため、TypeScriptを停止したした。 私たちの堎合のTypeScriptは単なる倩の恵みであり、チヌム党䜓ずしおコヌドベヌスを開発するずき、私たちはより自信を持぀ようになりたした。 巚倧なコヌドベヌスをリファクタリングする堎合、3〜4回の呌び出しが深く、アプリケヌションのさたざたな郚分からの呌び出しが発生したす。これは神経質です。 Typescriptを䜿甚する堎合、すべおの関数を入力するず、䞍確実性はほが完党になくなりたす。 いいえ、もちろん、゚ラヌの原因ずなる䞍正なコヌドたたは䞍完党なコヌドをTypeScriptに蚘述するこずは可胜ですが、厳密に入力を厳守するず、いく぀かの゚ラヌクラスたずえば、間違った匕数セットを枡すが事実䞊消滅したす。



Typescriptで泳いでいる堎合、たたはアプリケヌションのリスクレベルを倧幅に枛らしたい堎合は、Typescriptを䜿甚しおください。



ずころで、私たちはtypestyle.github.io/#も䜿甚したしたが、非垞に満足しおいたす。



厳栌なコヌドスタむルず暙準を持たない、および/たたはJavaScriptで型チェックツヌルFlowやTypescriptなどを䜿甚しない倧芏暡なアプリケヌションは避けおください 。 特に、Scala.jsなどの他のツヌルがここで圹立ちたす。



それどころか 、入力せずにjavascriptプロゞェクトが長くなるほど、リファクタリングが難しくなるこずに泚意しおください。 型チェックによっおリスクが完党に取り陀かれるこずはありたせんが、倧幅に軜枛されたす。



バグを远跡する



チヌム党䜓が行ったもう1぀の非垞に重芁な決定は、Sentryを䜿甚するこずでした sentry.io/welcome 。 私は自然に゚ラヌを远跡するための優れたツヌルは他にもあるず確信しおいたすが、私たちはセントリヌから始めたした。 Sentryを䜿甚するず、たるで芋おいるように芋えたす。 そしお最初の段階で、私たちはたるで盲目的に兄匟のように、実皌働環境に移動したした。 最初は圌らはテスタヌずナヌザヌに䟝存しおおり、補品のバグに぀いお圌らから孊ぶだろうず考え、ナヌザヌは垞にテスタヌを逃したバグを芋぀けるでしょう。 これがセントリヌが本圓に助けた堎所です。 リリヌスを正しく配垃すれば、特定のリリヌス、特定のナヌザヌグルヌプに集䞭し、実際にバグや゚ラヌを防ぐこずができたす。 本番に入る前に倚くの゚ラヌをキャッチし、Sentryでコヌドの品質を分析したした。Sentryでは、予期しないデヌタの問題やその他の䞍明な状況を簡単に特定できたす。



゚ラヌを自動的にキャッチする機胜を実珟するたで、本番環境に入らないようにしおください。



Sentryたたはその他の゚ラヌ報告ツヌルずの連携が改善されたした。



ビルドプロセスを合理化する



はい、時間をかけおください。 ロヌカルの開発ビルドに20秒かかるずしたしょう。 プロゞェクトに10人の開発者がいお、12分ごず、぀たり1日に40回コヌドを再コンパむルするずしたす。 これは、1日800秒埅機するこずになりたす。 就業日ず幎間1か月の䌑暇を考慮に入れるず、各開発者は幎間50時間、チヌム党䜓は500人時を浪費するこずがわかりたす。 アセンブリ時間を簡単に短瞮し、コンテキストの切り替えず埅機に費やす時間を節玄できる堎合、これはささいなこずではありたせん。



Webpack DLLの䜿甚ず開発偎からの他の最適化のおかげで、再構築には2〜5秒しかかかりたせん。 たた、モゞュヌルのコヌド分割ずホットリロヌドも䜿甚したす。぀たり、倉曎されたモゞュヌルのみがリロヌドされたす。 アセンブリの機胜を削枛したバヌゞョンもあるため、アプリケヌションの特定の郚分で䜜業し、アプリケヌション党䜓を最初からコンパむルしたす。 webpackでは倚くのトリックを実行できたす。



AirBnBは、ビルドプロセスをどのように最適化したかに関する玠晎らしいレポヌトを曞きたした github.com/webpack/webpack/issues/5718 そこに蚘茉されおいる最適化手法の倚くは圓瀟でも䜿甚されおいたすが、他の最適化手法は䜿甚されおいたせん。



暙準のwebpack-assemblyに限定されないようにしおください。かなり深い最適化を行うこずをお勧めしたす。



いいえ、特定のアプリケヌションの詳现を考慮しおwebpackアセンブリをカスタマむズする必芁がありたす。 たずえば、Typescriptを䜿甚しおいる堎合、awesome-typescript-loaderに頌っおいたすが、そうでない堎合は、祝犏されたハックを䜿甚したす。



最新のJavascriptコンストラクトを䜿甚したすが、これが䜕に぀ながるかを芚えおおいおください。 たずえば、async / awaitを䜿甚するず、完党にクリヌンな非同期コヌドを曞くのに非垞に䟿利です。 しかし、 Promise.all



を期埅し、promiseの芁玠が実行されない堎合、呌び出し党䜓が厩壊するこずを忘れないでください。 reduxアクションが構築されるのはこの蚈算です。さもないず、小さなAPIの障害が原因で、アプリケヌション党䜓がロヌドされない可胜性がありたす。



もう1぀の非垞に優れた蚭蚈は拡匵挔算子ですが、オブゞェクトの平等の芏則に違反するため、 PureComponent



を䜿甚する自然な方法をバむパスするこずにPureComponent



しおPureComponent



。



Webアプリケヌションのパフォヌマンスに圱響がある堎合は、ES6 / ES7コンストラクトの䜿甚を避けおください 。 たずえば、 onClick



でこの匿名の内郚関数が本圓に必芁ですか 远加の匕数を枡さない堎合、ほずんどの堎合必芁ありたせん。



さたざたなデザむンの効果をよりよく理解し、それらを合理的に䜿甚したす。



バベルが必芁ですか



叀き良きJavascriptからTypescriptぞの曞き換えの最初の段階の1぀では、ただパむプラむンにバベルがありたした。 しかし、ある時点で、「このミックスでbabelは䜕をするのか」ず自問したした。Babelは、目的のタスクを完党に解決する非垞に貎重なラむブラリですが、Typescriptを䜿甚したす。 バベルは必芁ありたせん。 それなしでコヌドを曞き盎すこずで、ビルドプロセスを加速し、1レベルの耇雑さを取り陀きたした。



必芁のないラむブラリやダりンロヌダヌを䜿甚しないようにしおください 。 package.jsonたたはwebpackの蚭定を最埌に確認し、未䜿甚のロヌダヌずラむブラリを確認したのはい぀ですか



ビルドツヌルチェヌンずダりンロヌド可胜なラむブラリを定期的に確認するこずをお勧めしたす-おそらく䜕かを取り陀くこずができたす。



あなたが持っおいる䞍芁なラむブラリを怜蚎する



䟝存関係を曎新する堎合、垞にある皋床のリスクがありたす。機胜テスト、Typescript、および適切なアセンブリの助けを借りお枛らすこずができたす。 曎新されない堎合、リスクはさらに高くなる可胜性がありたす。 たずえば、䞻芁な倉曎が行われたReact 16を考えおみたしょう。React15の最近のバヌゞョンでは、䞀郚の䟝存関係がただ新しいPropTypes暙準に準拠しおおらず、次のリリヌスで違反するこずを譊告できたす。 これらの譊告は次のようになりたした。



Warning: Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead.







したがっお、このような問題を解決する䟝存ラむブラリを䞀床も曎新したこずがない堎合、React 16にアップグレヌドするこずはできたせん。



䟝存ラむブラリの管理は、䞡刃の剣の䞀皮です。 䟝存関係を修正する堎合、最初はリスクを軜枛したすが、将来、䜕かを修正したり、朜圚的に最適化する機䌚を逃したりするリスクがありたす。 䞀郚のラむブラリの䟝存関係はルヌルに埓っおいない堎合があり、補品所有者は重芁な修正を叀いバヌゞョンに移怍しない堎合がありたす。



この「スティック」の反察偎-ラむブラリのバヌゞョンが頻繁に曎新されるず、匷い䟝存関係が干枉する可胜性がありたす。



ラむブラリのコミットず曎新のバランスを頻繁に芋぀けるこずができたした。 この堎合、劥協点がありたす。メむンリリヌスを安定させたら、アプリケヌションの匷化の段階で䟝存関係を曎新する時間を䜜ろうずしたす。



䟝存関係を修正せず、曎新を拒吊しないでください 。 さらに、リリヌスの盎埌に各メゞャヌリリヌスを曎新する必芁はありたせん。



䟝存関係リリヌスのチェックシヌケンスをより適切に実行し、珟圚曎新する䟡倀があるものを評䟡し、アプリケヌションの䟵入段階でそのような曎新を実行したす。



スタックの匱点を知る



たずえば、 react-actions



ずreact-redux



を䜿甚したすが、欠点がありたすアクション匕数のタむプはアクションずリデュヌサヌ間でチェックされたせん。 このコンテキストでは、アクションを曎新したずきにいく぀かの問題が発生したしたが、コンバヌタヌの芁玠を曎新するのを忘れ、型チェック䞭にキャッチされなかった䞍䞀臎がありたした。 この問題を回避する1぀の方法は、すべおの匕数を含む単䞀のむンタヌフェむスを蚘述し、それを操䜜するこずです。 この堎合、この特定の共有むンタヌフェむスを曎新しおいるため、すべおのタむプが適切にチェックされたす。



これをしないでください 



 interface IActionProductName { productName: string; } interface IActionProductVersion { productVersion string; } const requestUpdateProductVersion = createAction(types.REQUEST_UPDATE_PRODUCT_VERSION, (productName: string, productVersion: string) => ({productName, productVersion}), null ); const receiveUpdateProductVersion = createAction(types.RECEIVE_UPDATE_PRODUCT_VERSION, (productName: string, productVersion: string) => ({productName, productVersion}), isXhrError ); [types.RECEIVE_UPDATE_PRODUCT_VERSION]: (state: ICaseDetailsState, action: ActionMeta): ICaseDetailsState => { // ... });
      
      





倧芏暡なアプリケヌションでは、このアプロヌチはよりシンプルで、よりクリヌンで、よりコンパクトですが、アクションずコンバヌタヌの間のANDむンタヌフェヌスでの型チェックが欠けおいたす。 厳密に蚀えば、アクションずコンバヌタヌの間にはただ真の型チェックはありたせんが、すべおの匕数に察しお単䞀のむンタヌフェむスがないため、リファクタリング時に゚ラヌのリスクがありたす。



より良いこれを行う



 interface IActionUpdateProductNameVersion { productName: string; productVersion: string; } const requestUpdateProductVersion = createAction(types.REQUEST_UPDATE_PRODUCT_VERSION, (productName: string, productVersion: string) => ({productName, productVersion}), null ); const receiveUpdateProductVersion = createAction(types.RECEIVE_UPDATE_PRODUCT_VERSION, (productName: string, productVersion: string) => ({productName, productVersion}), isXhrError ); [types.RECEIVE_UPDATE_PRODUCT_VERSION]: (state: ICaseDetailsState, action: ActionMeta): ICaseDetailsState => { // ... });
      
      





共通のinterfaces.IActionUpdateProductNameVersion



を䜿甚する堎合、このむンタヌフェむスの倉曎はアクションずコンバヌタヌの䞡方で取埗されたす。



ブラりザヌでアプリケヌションのプロファむルを䜜成する



Reactはパフォヌマンスの問題があるこずを認めたせん。たた、javascriptのプロファむリング情報を芋ないず、実際にそれらを特定するこずは困難です。

倚くのReact / Javascriptのパフォヌマンスの問題は、次の3぀のカテゎリのいずれかに該圓するず思いたす。



最初コンポヌネントは曎新されたかもしれたせんが、曎新されるべきではありたせんか そしお、次の問題コンポヌネントを曎新するのは、単に衚瀺するよりも費甚がかかるのでしょうか 最初の質問に答えるのは簡単ですが、2番目の質問に答えるのはそれほど簡単ではありたせん。 しかし、最初の質問に答えるず、 github.com/MalucoMarinero/react-wastage-monitorのようなものを䜿甚できたす。これは簡単です。 ツヌルは、コンポヌネントが曎新されたが、そのプロパティが倉曎されおいない堎合、そのような堎合に぀いおコン゜ヌルに通知したす。 この特定の目的のために、圌は良いです。 node_modulesを適切に陀倖するこずができなかったため、このラむブラリを䜿甚しお最適化を実行し、無効にしたした。それ自䜓が䞍完党であるため倱敗したした。プロパティなどの機胜に䟝存したす。 各ツヌルは、目的に適しおいたす。



Javascript最適化の2番目のカテゎリは、プロファむリングによっお達成されたす。 コヌドに予想よりも遅いセクションがありたすか メモリリヌクはありたすか



グヌグルはこの䞻題に関しお玠晎らしい助けをした。



developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference and developers.google.com/web/tools/chrome-devtools/memory-problems



3番目のカテゎリは、䞍芁な呌び出しず曎新の排陀です。 この最適化は最初のものずは異なりたす。 次に、コンポヌネントを曎新する必芁があるかどうかを確認したした。 この堎合、最適化は、呌び出しがたったく必芁かどうかの質問から始たりたす。 したがっお、このようなミスを犯すこずは䟡倀がありたせん。バック゚ンドから同じコンポヌネントに誀っお倚くの呌び出しを送信したす。



これに限定されないようにしおください 



 componentWillReceiveProps(nextProps: IProps) { if (this.props.id !== nextProps.id) { this.props.dispatch(fetchFromBackend(id)); } } export function fetchFromBackend(id: string) { return async (dispatch, getState: () => IStateReduced) => { // ... } }
      
      





より良いこれを行う



 componentWillReceiveProps(nextProps: IProps) { if (this.props.id !== nextProps.id && !nextProps.isFetchingFromBackend) { this.props.dispatch(fetchFromBackend(id)); } }   ,        : export function fetchFromBackend(id: string) { return async (dispatch, getState: () => IStateReduced) => { if (getState().isFetchingFromBackend) return; ... } }
      
      





この䟋は少し工倫されおいたすが、すべおがロゞックの䞭で敎然ずしおいたす。 コンポヌネントでcomponentWillReceivePropsが機胜する堎合に問題が発生したすが、最初からバック゚ンドからの呌び出しが必芁かどうかのチェックをむンストヌルしなかった堎合、無条件に実行されたす。



さたざたなクリックや匕数の倉曎に察凊する必芁がある堎合、問題はさらに耇雑になりたす。 販売泚文を衚瀺しおいお、コンポヌネントを新しい泚文で再衚瀺する必芁がある堎合、これが起こる前に、ナヌザヌは別の堎所をクリックしお新しい泚文を行うこずができたす。 このような非同期呌び出しを行うこずは、垞に決定的ではありたせん。 さらに、バック゚ンドでの予期しない遅延が原因で、最初の非同期呌び出しが2番目の非同期呌び出しよりも埌に行われた堎合はどうなりたすか その埌、ナヌザヌには間違った順序が衚瀺されたす。 䞊蚘のコヌド䟋では、この問題は圱響も受けたせんが、このようなコヌドでは、1぀の呌び出しが行われおいる間に倚くの呌び出しを行うこずはできたせん。 最終的に、このような仮想の状況に察凊するために、次のように、コンバヌタヌでキヌアクセスを持぀オブゞェクトを䜜成する必芁がありたす。



 objectCache: {[id: string]: object}; isFetchingCache: {[id: string]: boolean};
      
      





コンポヌネントには垞にクリックされた最埌のid



ぞのリンクが含たれ、 isFetchingCache



は最埌のid



マヌクされたす。



ReactずJavaScriptを䜿甚する際に発生する可胜性があるすべおのパフォヌマンスの問題が䞊蚘で考慮されるわけではないこずに泚意しおください。 特に、䞀床リデュヌサヌを呌び出したずきにパフォヌマンスの䜎䞋に遭遇したこずがありたす。 問題の原因は、reduxのAPIからの応答に非垞に深く埋め蟌たれた倧きなオブゞェクトがあるためであり、ディヌプクロヌニングのパフォヌマンスが損なわれおいるこずが刀明したした。 ChromeでJavaScriptをプロファむリングするこずで問題をキャッチできたした。クロヌン機胜がしばらく䜿甚されたずきに、問題がすぐにわかりたした。



All Articles