こんにちは! 中部オフィスでエンジニアをやっているichienです。
5月に入社してfreeeプロジェクト管理の開発を担当していました。
今回はfreeeプロジェクト管理のJS(JavaScript)バンドルサイズを削減した話を紹介します。
改善前はバンドルしたJSファイルが5MB
以上に肥大化しておりダウンロードに数秒かかっていました。
その分、ファーストビューの表示もその分遅くなり、ユーザ体験に課題がありました。
今回は次の3つの施策を実施して、5MB
→1.7MB
まで削減し、ダウンロード時間も70%
短縮できた話をします。
- gzip圧縮の適応
- webpack-bundle-analyzerで現状の可視化と削減対象の決定
- 不要な日付・祝日データの排除
また、今回の取り組みは一度にすべて対応したわけではありません。次の様にstep-by-stepで進めることを意識しました。
- 現状を可視化し理解する
- 着目点を決める
- 小さな対応を多くやる
では、次から実施内容を紹介していきます!
JSファイルのgzip圧縮
巨大バンドルサイズの原因を探るため、Chrome DevToolsのNetworkタブを確認しました。
バンドルファイルのレスポンスヘッダーにcontent-encoding:gzip
がなかったことが、問題特定の決め手になりました。
freeeプロジェクト管理はRuby on Rails+NGINXの構成で稼働しており、JSファイルなどのコンテンツも配布しています。
Railsはデフォルトでコンテンツの圧縮は行わないが、NGINXのngx_http_gzip_module
を利用することでgzip圧縮を適応することができます。
プロダクトのコードでは、既に次の形で設定がありました。
- 修正前
# gzip圧縮を有効化 gzip on; # gzip圧縮する対象のコンテンツタイプを指定 gzip_types application/javascript text/javascript;
解決策は漏れていた設定追加だった
解決策はgzip_types
にapplication/x-javascript
を追加しただけです。
NGINXのモジュールで利用しているpassengerがJSファイルをapplication/x-javascript
のコンテンツタイプで配信していました。
これに対する設定漏れが原因でした。
修正後のnginx.conf.erb
は以下となります。
- 修正後
gzip on;
# application/x-javascriptを追加
gzip_types application/x-javascript application/javascript text/javascript;
原因特定の流れ
原因特定のため、私が辿っていったコードの流れを紹介しておきます。
nginx.config.erb
のhttpブロックでpassengerのhttp.erb
の読み込みを確認。
http { <%= include_passenger_internal_template('http.erb', 2) %>
http.erb
の2行目でmime.types
の読み込みを確認。
include '<%= PhusionPassenger.resources_dir %>/mime.types';
- 最後に、
mime.types
の8行目でjsファイルとcontent-typeの対応を確認。
types { application/x-javascript js;
webpack-bundle-analyzerで現状の可視化と削減対象の決定
次に、更に削減可能な箇所を特定するためwebpack-bundle-analyzerを使ってバンドルされているモジュールサイズの可視化を行いました。
今回はファイルサイズが巨大なJSONファイルに着目し、以下のライブラリのファイル削減方法を紹介します。
- date-holidaysのholidays.json (
447KB
) - moment-timezoneのlatest.json (
184KB
)
date-holidaysの祝日情報を日本のみに限定
freeeプロジェクト管理では、日付ごとに工数入力できる機能があります。 その日の祝日判定にdate-holidaysを使っています。
その中で447KB
もあるholidays.json
を削減する方法を調査しました。
調べてみると、このholidays.json
は世界の祝日情報を持っていました。
プロダクトの要件として日本の祝日情報が判定できれば良いので、日本だけに限定できれば大幅削減可能と考えられます。
ライブラリ側で対象国を絞り込めるholidays2json
が用意されています。
これをインストール時にnpm post scriptで実行する形を取りました。
これでnpm install
やyarn install
完了後に自動で実行され、データが削減された状態になります。
"scripts": { "postinstall": "holidays2json --pick JP --min" }
対応前後のholidays.json
単体で比較すると、444kb
→44kb
となり大幅に小さくできました。
moment-timezoneも日本のデータに絞る
moment-timezoneに含まれるlatest.json
のファイルサイズも186KB
とかなり大きいです。
こちらも日本の情報に絞ることでファイルサイズを削減します。
対象国を絞るには、moment-timezone-data-webpack-pluginが利用できます。
webpackの設定例は以下となります。
const MomentTimezoneDataPlugin = require('moment-timezone-data-webpack-plugin'); module.exports = { plugins: [ new MomentTimezoneDataPlugin({ matchCountries: 'JP' }), ], };
結果、moment-timezone自体のデータサイズが186KB
→17KB
となり、約1/10に削減できました。
まとめ
紹介した3つの方法で、バンドルサイズを5MB
→1.7MB
まで削減でき、ファイルダウンロード時間も70%
短縮できました。
lighthouseのPerformanceスコアを28
→59
まで改善することができました。
改善前 | 改善後 |
---|---|
自分が貢献できる箇所を探し出し、小さな改善を複数行い、最終的に大きなインパクトを作り出すことができました。
振り返ると、分析と可視化をちゃんとステップに含め、成果を修正前後で比較してわかりやすい形にできたのが良かったと考えています。
改善はもちろんこれで終わりではありません。継続して課題の可視化と解決に取り組んで行きます!
最後まで読んでいただきありがとうございました。