🪦CSS in JSでパフォーマンスがめちゃくちゃ悪くなった話

CSS in JSでパフォーマンスがめちゃくちゃ悪くなった話 フロントエンド

はじめに

僕が普段NextJSとかでスタイルを描く時はmodule cssを使ってるんですが、案件先でmaterialUIのmakeStylesを使っていました。
あんまりCSS in JSは使ったことがなかったのでウキウキで開発していたら、パフォーマンスを悪化させてしまい、ユーザーが機能を使えなくなったという障害を起こしたことがあるので、ここで供養させてください。

CSS in JSを使っててパフォーマンスを意識したことがないよって方は参考になると思いますので、是非読んでいってください。
フロントエンド中級者以上の方も是非読んでいただきたいです。
修正のPRを出した時に、何でパフォーマンス改善できるかベテランの人でもすぐには分からなかったレベルですので!
( たぶん僕のように発生させる人が稀なだけ )

事象

障害が起きた画面ではテーブルの描画を行なっており、サイズは大体100行 x 10列くらいでした。
テーブルのtd要素ではTextコンポーネントを呼び出しており、1セルあたり3つのTextコンポーネントを利用していました。

今回、パフォーマンスが悪かったのはこのTextコンポーネントで、普段は問題なかったけど、3000個も生成するとパフォーマンスの悪さが露呈した感じです。

該当コード・原因

ポイントは9行目と15行目です。
makeStylesは引数を取ることができ、引数をもとにスタイルを生成することができます。

import { makeStyles } from '@material-ui/core/styles';

type TextProp = {
  children: React.ReactNode;
  color: "black" | "red" | "blue";
}

const calcStyle = makeStyles(() => ({
  text: ({ color }: { color: "black" | "red" | "blue" }) => ({
    color: color,
  })
}))

export const Text = ({ children, color }: TextProp) => {
  return { children }
};

これを仮に以下のコードで利用したとします。
blue, black, redを各10回、計30回Textコンポーネントを呼び出してます。

export const Page = () => (
  {
    [...Array(10)].map( _ => ["blue", "black", "red"])
      .flat()
      .map((color) => {color})
  }
)

描画結果はこんな感じ

Textコンポーネント描画

chrome dev toolでスタイルを確認してみると、makeStyles-text-xxxというclassが1コンポーネントにつき、1つ割り当てられてます。(全部違うclass)

HTMLスクリーンショット
HTMLスクリーンショット

そしてそれに紐づくcssも全て生成されています。

cssスクリーンショット
cssスクリーンショット

今回の例では30回Textコンポーネントを呼び出しているので、30種類cssが生成されていたことになります。
本来ならblue, black, redの3種類でいいはずです。

解決策

makeStylesで引数を取るのをやめました。
以下が例です。

import { makeStyles } from '@material-ui/core/styles';

type TextProp = {
  children: React.ReactNode;
  color: "black" | "red" | "blue";
}

const calcStyle = makeStyles(() => ({
  blue: {
    color: "blue",
  },
  black: {
    color: "black",
  },
  red: {
    color: "red",
  },
}))

export const Text = ({ children, color }: TextProp) => {
  const colorizeStyles = calcStyle();
  return { children }
};

生成されるHTMLはこんな感じ
blue, black, redに対応した合計3種類だけのcssが生成されていることがわかります。

改良版Textコンポーネント
改良版Textコンポーネント

色ごとにスタイルを記述するのが手間ですが、カラーパレットが存在しているプロジェクトなどでは、変な色のテキストを生成しなくなるので、かえって安心のような気もします。

他のemotionなども同じ原因でパフォーマンスは悪くなり得ます。
CSS in JSは便利ですけどパフォーマンスには気をつけないとですね。

同じコンポーネントを大量に呼び出す画面を作ることはあんまりなく、3000回呼び出すなどのケースでようやく気付くパターンは往々にしてあるように思います。
いまいちど、自分のプロジェクトのソースを確認してみてください。
原因がわかるまでかなり時間がかかりました。。
たまたまdev toolを見てみようと思わなかったら絶対気づかなかったです。

タイトルとURLをコピーしました