본문

Tech
React+WebpackでCode Splittingの適用までの流れ

작성일 2019.12.04

blog_44_main.webp

作業の背景

こんにちは。私はWhaTapでフロントエンド開発を進行している、ウォンソンチョルと申します。WhaTapのフロントは現在、Spring boot + Velocityの環境からReactに進んでいて、私はその過程でWhaTapに入り、現在多くのlegacyコードをReactに合わせて変換しています。

Reactは、従来のページごとにHTMLファイルを使ってドームを作成する方法ではなく、JavaScriptでページの要素を描くSPA(Single Page Application)なので、スクリプトの比重がとても高く、多くのスクリプトファイルをインポートする場合にページロードにボトルネックが発生する可能性があり、通常はWebpackを使用してスクリプトファイルを1つのBundleファイルにする作業を行ないます。

Webpackを利用してボトルネックを減らしましたが、サービスの規模が大きくなり、すべてのスクリプトファイルを合わせたBundleのサイズが大きくなりました。Bundleのサイズが大きくなり、ページの最初の進入が遅くなる不便が発生するようになり、この不便を解決するためにCode Splittingという技術を利用してすべてのコードの集合だったBundleのコードを特定の基準で分けてChunkという特定スクリプト とモジュールのサブセットとなるファイルに分ける作業が必要になりました。

WhaTapのサービスは react-routerを利用してアドレスによって異なるコンポーネントを呼び出すようになっていますが、このアドレスによって呼び出されるコンポーネントを基準にChunkを分けるようにCode Splitting作業をしてみましょう。

現在の状況を把握

まず、現在のwebpackの設定ファイルを確認します。

blog_44_01.webp

私たちのサービスの場合にはこのように production build, development build 設定が分かれていました。開発環境と配布環境の設定を分けるのは良いですが、両方の設定に重複する内容も多く、内容追加時にも両方に同じ作業をしなければなりません。開発環境と展開環境の設定がもし異なっていても、配布時に開発環境では確認できないバグが発生する可能性があるため、作業をする謙にひとつにまとめていただきました。

blog_44_02.webp

ファイルはひとつにまとめて、 envに分岐してビルド時の命令語によって production build, development buildの設定を異ならせることができるようにしました。他には変わるとサイドエフェクトが発生する可能性があるため、設定はすべて同じに指定し、複数のプラグインや設定がサービスで行っている役割だけを簡単に把握して注釈を付けました。

そして、現在ビルドされた結果を確認するためにwebpack-bundle-analyzerというプラグインを追加して結果を確認しました。

blog_44_03.webp

フロントのすべてのスクリプトがmain.bundle.jsというファイルに含まれていることがわかります。サイズはnode_modulesの比重が最も大きかったし、ノードモジュールでは依存性で重複した内容のあるモジュール、実際にほとんど使用しないがサイズの大きいモジュールなども確認できました。

blog_44_04.png

マウスを置くと、ファイルのサイズを詳しく確認できます。

  • Stat size(minificationなどの変換が起こる以前に入力されたサイズ)
  • Parsed size(変換後に出力されたファイルのサイズ
  • Gzipped size(gzip圧縮後のサイズ)

(弊社サービスは閉鎖網からパッケージ形態でも製品をサポートするため、ネットワークは高速ですが、PCの性能は良くない場合、gzipファイルの解凍に時間がかかる場合があり、21.85MBのファイルを実際利用 テストしてからgzipを利用する予定はあります。)

21.85MBという大きなサイズのバンドルファイルを確認できました。

Code Splitting適用

まず、リアクト公式文書である、(https://reactjs.org/docs/code-splitting.html#import)にも書かれているように、コードスプリットを適用するのにdynamic import方式が良いと判断され、コンポーネントがレンダリングされるタイミングにdynamic importを行なうHOCを作成しました。

blog_44_05.webp

そして実際のreact-routerで利用するコンポーネントを上記のHOCを利用してdynamic importするように修正しました。

blog_44_06.webp

分離がうまくいっただろう? と思っててビルドをした結果

blog_44_07.webp

従来と同じ結果が出たことが確認できました。(テストの場合は、より早く結果を確認するためにminimizeオプションを使用せずにテストして容量が大きく表示されています。) なぜ適用されなかったのか、webpackの設定を見てみると、dynamic-import-nodeというプラグインがbabelで利用されていることを確認しました。

dynamic import 構文をrequireに変える役割を果たすプラグインですが、検索してみるとnodeサーバー側で使用するプラグインであり、これまでフロントでdynamic importを利用した部分もないから、悩みなく除去しました。

blog_44_08.webp

main.bundle.jsにあった要素がたくさん消え、chunkファイルにその消えた内容がある様子です。そして、再度minimizeオプションを使用した状態で結果を確認してみたら、

blog_44_09.png

まだ大きなサイズですが、最初と比べて容量が確実に減ったことが確認できます。

caption:(該当作業当時のスクリーンショットがなく、現在ワタップサービスで適用されている姿に置き換えます。サービスでは当時よりも少し改善された状況です。)

実際のページでもmain.bundle.jsファイルを読み込み、状況別にチャンクファイルを追加読み込むことを確認できます。

おわりに

しばらくの間、機能開発、バグ修正、機能開発、バグ修正だけが連続した日々でしたが、Webパックについて完全に文外だった私がこのように改善をすることになって学んだことも多く、楽しい(+大変な)作業であり、bundle analyzerなど視覚的に状況を 見せるツールを使ってみると、スプリット以外にも多くの改善点が見られ、今後も多くの改善作業を行なう予定です。

今回は本当に簡単にCode Splittingを導入してみましたが、実際にここまで扱った内容は本当に簡単に導入のみできる内容でしたので、この内容に加えて、特定のモジュールだけで使用するモジュールをどのように分離するのか、chunkを呼び出すタイミング調整、dynamic import構文のes5での利用など、本当に扱わなければならない内容が多いです。その内容は次にまた機会があれば取上げたいと思います。

足りない筆力の文をご覧いただき、ありがとうございます。今後も日々に発展していくWhaTapのフロントになるように頑張ります。

ありがとうございました。

WhaTap Monitoringを体験してみましょう。
難しかったモニタリングと分析が容易に実現できます。