
こんにちは、ミツカリCTOの塚本こと、つかびー(@tsukaby0) です。
当社では今までCSSライブラリとしてEmotionを使用していました。
Emotionを選定したのは2022年8月頃で、その頃はまだTailwindよりも人気でした。他にMUIを利用することも決めており、MUIもEmotionを利用しているため、丁度よいという理由もありました。
CSS-in-JSライブラリであるため、開発体験としては気に入っていましたが、ランタイム時に(ブラウザでページアクセス時に)JSが動いてスタイルが計算され、styleタグが挿入されるため、パフォーマンスの問題があります。
以前のこちらの記事では、当社の田中からStyelXとEmotionの共存設定やトラブルシューティングについて解説しました。
上記記事の通り、最近当社ではEmotionからStyleXへ乗り換えることを決定しました。今回の記事ではパフォーマンス計測をしてみたという話をしようと思います。
結論
JSの実行時間を 30.8% (1,031ms → 713ms) 減らすことができました。
Emotionのパフォーマンス問題
Emotionはランタイム時にスタイルが計算され、styleタグがページに挿入されます。例えば以下は実際に当社のサイトでページアクセスした後のHTMLです。

Emotionによって挿入されたstyleタグが沢山あります(画面に収まっていないですが、まだ沢山あります)。
これだけの量をJSで計算して挿入している場合、パフォーマンスが気になります。
最近ではこのような問題からZero RuntimeのCSSライブラリが人気です。例えばvanilla-extractやPanda CSS、Linariaなどがあります。
(ちなみにTailwindもZero Runtimeですが、左記のものとは異なりCSS in JSではありません)
Zero Runtimeに乗り換えた方が良いと思うエンジニアは多いと思いますが、実際に乗り換えた場合にどれくらい早くなるか知っている、あるいは試したことがある人は少ないのではないでしょうか。
私もそのうちの一人なので、今回実験してみることにしました。
実験
今回は当社のサービスサイトを実際に1ページ移行してみて、パフォーマンスの変化を計測したいと思います。
対象とするページは料金ページです。なお、この記事を読まれている現段階ではまだ移行版がリリースされていないかもしれませんし、また別のライブラリに乗り換えているかもしれないのでご注意ください。
まずはmasterブランチの状態でlocalでサービスサイトを起動し(next dev)、そこに対してChromeの開発者ツールを使って計測します。その次にStyleX化したbranchを用意して、そこで同じように計測します。
今回はZero Runtimeになるわけですから、JSの実行時間が減るはずです。Chromeの開発者ツールでそれを測れるので、実際に見てみます。
計測環境
- 計測ツール: Chrome DevTools(Performance タブ、Lighthouse)
- 計測条件:
- Chrome バージョン: 144.0.7559.110 arm64
- デバイス: Macbook Air M2 2022 (Desktop)
- ネットワーク: スロットルなし
- CPU: スロットルなし
- キャッシュ: 無効化
- 3回計測の中央値を採用
計測項目
- Scripting時間: JavaScriptの実行時間(Performance タブ)
- Rendering時間: スタイル計算・レイアウト時間(Performance タブ)
- Total Blocking Time (TBT): メインスレッドのブロック時間(Lighthouse)
- Largest Contentful Paint (LCP): 最大コンテンツの描画時間(Lighthouse)
- First Contentful Paint (FCP): 最初のコンテンツ描画時間(Lighthouse)
この辺りについては以下の記事などweb.devやdeveloper.chrome.comなどを参考にすると良いです。
計測結果
では実際に計測してみます。
Before(Emotion版)
まずはEmotion版です。
Performance タブ(3回計測)
| 回 | スクリプト | レンダリング | ペイント | 合計 |
|---|---|---|---|---|
| 1回目 | 1,031ms | 54ms | 31ms | 5,946ms |
| 2回目 | 1,024ms | 47ms | 25ms | 5,094ms |
| 3回目 | 1,040ms | 57ms | 40ms | 7,600ms |
| 中央値 | 1,031ms | 54ms | 31ms | 5,946ms |
Lighthouse スコア(3回計測)
| 回 | Score | FCP | LCP | TBT | Speed Index |
|---|---|---|---|---|---|
| 1回目 | 46 | 1.2s | 5.9s | 690ms | 1.5s |
| 2回目 | 46 | 1.2s | 5.8s | 710ms | 1.5s |
| 3回目 | 49 | 1.2s | 5.8s | 580ms | 1.4s |
| 中央値 | 46 | 1.2s | 5.8s | 690ms | 1.5s |
After(StyleX版)
EmotionからStyleXへの書き換え作業は特筆することがないので割愛します。
なお、MUIを使っていると、それがEmotionを使用しているので、完全にEmotionを排除するにはなかなか骨が折れます。
完全に置き換わったかどうかはページを開いてChrome DevツールのElementタブで emotion で検索すると分かります。置き換えに成功しているとemotionによるstyleタグがなくなっています。
Performance タブ(3回計測)
| 回 | スクリプト | レンダリング | ペイント | 合計 |
|---|---|---|---|---|
| 1回目 | 698ms | 57ms | 29ms | 7,906ms |
| 2回目 | 739ms | 56ms | 29ms | 4,277ms |
| 3回目 | 713ms | 47ms | 26ms | 4,871ms |
| 中央値 | 713ms | 56ms | 29ms | 4,871ms |
Lighthouse スコア(3回計測)
| 回 | Score | FCP | LCP | TBT | Speed Index |
|---|---|---|---|---|---|
| 1回目 | 53 | 1.3s | 5.6s | 410ms | 1.5s |
| 2回目 | 55 | 1.3s | 5.6s | 390ms | 1.5s |
| 3回目 | 58 | 1.3s | 5.5s | 340ms | 1.4s |
| 中央値 | 55 | 1.3s | 5.6s | 390ms | 1.5s |
比較まとめ
Performance タブ(中央値)
| 項目 | Before | After | 変化 |
|---|---|---|---|
| Scripting | 1,031ms | 713ms | -318ms(-30.8%) |
| Rendering | 54ms | 56ms | +2ms(±0) |
| Painting | 31ms | 29ms | -2ms(±0) |
Lighthouse(中央値)
| 項目 | Before | After | 変化 |
|---|---|---|---|
| Performance Score | 46 | 55 | +9pt(+19.6%) |
| TBT | 690ms | 390ms | -300ms(-43.5%) |
| LCP | 5.8s | 5.6s | -0.2s(-3.4%) |
| FCP | 1.2s | 1.3s | +0.1s(誤差範囲) |
| Speed Index | 1.5s | 1.5s | ±0 |
素晴らしいですね、各種スコアが改善されているのでZero Runtimeは効果があると言えそうです。EmotionからStyleXへの移行により、以下の改善が確認できました。
- Scripting時間: 1,031ms → 713ms(-30.8%)
- Performance Score: 46 → 55(+19.6%)
- TBT(Total Blocking Time): 690ms → 390ms(-43.5%)
- LCP: 5.8s → 5.6s(-3.4%)
FCPが若干悪化(+0.1s)していますが、これは計測誤差の範囲内と考えられます。
注意点
バンドルサイズについて
今回は1ページのみの移行のため、Emotionのランタイムライブラリは削除されていません。他のページがEmotionを使用している限り、共通チャンクにEmotionは残り続けます。
全ページをStyleXに移行した場合、JSのバンドルサイズを少し減らすことができます。
next devとLighthouseの低スコアについて
今回対象としたページをLighthouseを使って本番環境で分析するとLCPなどのスコアが大分改善された状態で表示されます。
next devは開発用のモードであり、様々なオーバーヘッドがあるため、正確な計測がしたい場合は本番環境に出してから実施するか next build と next start を使うとよいです。
今回の検証ではBeforeもAfterも同じ next dev なので、差分の計測には問題ありません。
おわり
流石に早くなるだろうと思ってやってみた実験でしたが、想像よりも早くなって少し驚きました。
Scriptingの30.8%削減とTBTの43.5%改善 は顕著ですね。Emotionのランタイムでのスタイル計算がなくなり、JavaScriptの実行時間とメインスレッドのブロック時間が大幅に減少したと想像できます。
頻繁に再レンダリングされるコンポーネントでも効果を発揮しそうです。サービスサイトやLPではそのようなコンポーネントは少ないでしょうが、Webアプリ内部の方では効果がありそうです。
StyleXへの移行はまだ始まったばかりですが、時間を見つけて徐々に移行していきたいと思います。
現在、ミツカリではITエンジニアを募集しています。興味のある方はぜひお気軽にご連絡ください!