11月1日から27日まで開催されていた、webパフォーマンス改善のコンテスト「Web Speed Hackathon 2022 Public」に飛び入り参加してきました。
仕事の方ではバックエンド側ということもあり、フロントのパフォーマンス改善についてあまり詳しくはなかったのですが、先駆者の方々のブログやリポジトリを拝見しながらなんとか取り組んでみました。
初回計測は1点...
とりあえずLighthouseで計測してみましたがまさかの1点!
動作を確認するために操作するのにもストレスがかかるレベルの重さでした
これをなんとか改善していきましょう。
webpack.config修正
まずはビルドをproductionとdevlopmentに分割するために、以下の構造と設定にしました。
root/
├ webpack.common.js // 汎用設定
├ webpack.dev.js // 開発用設定
├ webpack.prod.js // 本番用設定
└ webpack.server.js // サーバーサイド用
"build": "yarn build:server && webpack --config webpack.prod.js",
"build:server": "webpack --config webpack.server.js",
"build:dev": "yarn build:server && webpack --config webpack.dev.js",
"build:watch": "yarn build:dev --watch --progress",
webpackのファイル同士の設定をマージすることが出来る webpack-mergeというモジュールを使用し、本番・開発用に汎用設定をマージすることでコマンドで使い分けることが出来るようにしました。
次に、devlopmentにwebpack-bundle-analyzer
を導入しバンドルされたファイルのサイズを可視化します。こちらが出力結果です。
表示されているsizeの概要は以下の通りです。
- stat size: 圧縮前のソースコードのサイズ
- parsed size: コンパイル後のバンドルファイルのサイズ
- gzipped size: gzip圧縮後のサイズ
とりあえず置き換え可能でありサイズが大きい物から削っていきました。
バンドルサイズを削る
Zengin-codeをモジュールからAPIへの取得に
parsed size: -約3.1MB
zengin-codeは一部で使用されるダイアログコンポーネントでのみ使用されており、すべてのページで使用されているわけではありません。そのため、ダイアログ起動時のタイミングで全銀システムが提供するAPIからデータ取得を行う方式にしました。
ブラウザ対応関連のものを無くす
parsed size: -約1.23MB
レギュレーションにGoogle Chrome 最新版
での動作が担保出来ればよい、と記述されていたため古いブラウザ対応のために使用されている以下moduleを削除しました。
- core-js
- es5-shim
- es6-shim
- es7-shim
Fort AwesomeをモジュールからSVGへ
parsed size: -約788KB
アイコンにはFort Awesomeというアイコンフォントを提供するサービスが使用されていますが、アプリケーションでは3カ所でしか使用されていなかったため削ります。
モジュールによる提供だけではなくsvgによる提供を行っていたため、こちらに変更しました。
Momentjsからdayjsへ
parsed size: -約689KB
Momentjsはバンドルサイズが大きいため、互換性が高くMomentjsと比べサイズの小さいDayjsに置き換えました。
また、サイズという観点以外にもMomentjsは2020年にメンテナンスモードに入っているためこちらの点からも置き換えるべきでしょう。
lodashをやめる
parsed size: -約480KB
lodashで使用されているほとんどのメソッドは、ES20xxで追加されたメソッドを使用することで置き換え可能です。
下記のリポジトリを参考にしながら置き換えていきました。
バンドルサイズ変更後
変更前の 9.49MBから1.98MBまで削ることが出来ました!🎉
これ以外にもaxiosの削除・Preactの導入なども考えられましたが、今回は時間と知識の問題でここまでは行いませんでした...
画像・フォント最適化
画像最適化
imagesフォルダが85MBとかなり大きいのでこちらを最適化していきます
デフォルトでは画像をトリミングするコンポーネントを使用することで、使用箇所に合わせたリサイズを行っていましたが、最初からサイズに適した画像をこちらで準備すればコンポーネントで処理を行う必要がありません。
画像最適化ソフトは使ったことがなかったので、どれを選択すれば良いか困りましたが今回は「squoosh」を使用しました。採択理由は@squoosh/cli
を使用することで複数画像をまとめてリサイズ・変換が可能だったためです。
squoosh-cli --webp '{quality: 70}' *.jpg
@squoosh/cli
の使用方法に関してはこちらが参考になりました
全ての画像をリサイズ・圧縮し、jepgからwebpに変換することで85MB -> 225KBまで削ることが出来ました
フォント最適化
一部のページでwebフォントが使用されていましたが5.8MBと大きいのでこちらも削ります。まずはttfからwoff2に変換します。woff2は初めて知りましたがwoffと比べて 圧縮アルゴリズムとしてBrotli方式を採用していることで、woffと比べて12%程度圧縮率が高いらしいです。
さらに、フォントを使用している個所を確認すると数字と一部記号のみでしか使われていないためサブセット化を行い、使用されていないフォントを削除することでサイズを5.8MB -> 3.8KBまで削れました。
その他
gzip圧縮を行う
バックエンド側のレスポンスをgzip圧縮してから配信することでデータサイズを小さくします。
v3のfastifyでは、fastify-compressを使用することで実現可能です。
import fastifyCompress from "fastify-compress";
server.register(fastifyCompress, {
encodings: ["gzip", "deflate"],
});
CLSの改善
CLSとはgoogleが提唱する「Core Web Vitals(CWV)」というウェブパフォーマンスに関する指標の一つあり、ページの安定性を示すものです。
こちらの指標を向上させるために、以下の変更を行いました。
- 画像サイズを指定しておく
- サイズを指定することで画像が読み込まれる前から領域を確保できる
- loding表示中の際に表示領域を指定しておく
- loding完了後にfooterが大きく動くことを防ぐため、
height: 100vh
を指定
- loding完了後にfooterが大きく動くことを防ぐため、
画像の遅延読み込み
最初に読み込まれる画像以外にloading="lazy"
を指定することで、一部画像の遅延読み込みを行います。遅延読み込みを行うことでページの表示速度高速化につながります。
font-display: swap
フォントの読み込みが終わるまでは代替えフォントで文字を表示します。文字を最初から表示させておくことでレイアウトシフトを予防しCLS改善に繋がります
browserslistの更新
こちらと同様の理由でbrowserslistに最新版のchromeを指定しました。こちらを指定することでBableとPostCssのトランスパイル時に サイズを最小限にしてくれます。
結果
Web Speed Hackathonでの点数は 459.75/500点という結果でした!!
450点超えた~!
— 吉田 (@vitamin_yoshida) November 27, 2022
"Web Speed Hackathon 2022 Public" に挑戦中です!
スコア 459.75 / 500.00 で、現在 13 位です #WebSpeedHackathonhttps://t.co/Bzq8ekPctW #WebSpeedHackathon
lighthouseで見ても改善前と比べ大幅にパフォーマンスが上がっていますね!
300点を超えるのが目標だったので個人的には満足です。ただ採点にはややブレがあるため上振れた感は否めません(笑)
反省点
バンドルサイズの軽減・画像とフォントの最適化以外のことは余りできなかったのは心残りです。
特にmemo化やCloudFlareのCDNを利用した高速化はやってみたのですが期限までに実装できませんでした。
また、画像のリサイズ周りでVPTに何度も引っかかってしまい大幅に時間を取ってしまいました... ローカルでテストを実行する方法があったようなので、次の機会がこちらを試してみたいです
感想
今後はこちらのブログを見ながらどのような改善点があったのか確認していきたいと思います!
フロント側の速度改善方法を実践的に学ぶことが出来てとても楽しかったです!
開催してくださったCyberAgentさんありがとうございました!今まで無料デプロイを提供していたherokuにも感謝!