はじめに
僕が普段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})
}
)
描画結果はこんな感じ
chrome dev toolでスタイルを確認してみると、makeStyles-text-xxx
というclassが1コンポーネントにつき、1つ割り当てられてます。(全部違うclass)
そしてそれに紐づく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が生成されていることがわかります。
色ごとにスタイルを記述するのが手間ですが、カラーパレットが存在しているプロジェクトなどでは、変な色のテキストを生成しなくなるので、かえって安心のような気もします。
他のemotion
なども同じ原因でパフォーマンスは悪くなり得ます。
CSS in JSは便利ですけどパフォーマンスには気をつけないとですね。
同じコンポーネントを大量に呼び出す画面を作ることはあんまりなく、3000回呼び出すなどのケースでようやく気付くパターンは往々にしてあるように思います。
いまいちど、自分のプロジェクトのソースを確認してみてください。
原因がわかるまでかなり時間がかかりました。。
たまたまdev toolを見てみようと思わなかったら絶対気づかなかったです。