InfoQの記事を読んでいるとCSS-in-JSに関して以下のように書いてありました。
スタイル付けされたCSS-in-JS実装は、スタイル付けされていないバージョンと比べて50%以上もレンダリングに時間がかかるように見えた。
CSS-in-JSはパフォーマンスが落ちるとよく聞くものの、実際どうなのか知りたかったので計測してみました。
計測方法
CSS-in-JSを使って作成したコンポーネントとそうではないコンポーネントのレンダリングに掛かる時間を計測して比較します。
CSS-in-JSのライブラリはemotionとstyled-componentsを採用しました。
計測には以下のコードを使用します。
Appをレンダリングすると、画面上部にボタン, その下にNormalDiv
コンポーネントがNUM
の数だけ表示されます。
ボタンを押下するとNormalDiv
コンポーネントが再レンダリングされます。
再レンダリングが終わるとボタンを押下してから再レンダリングまでにかかった時間を計測してコンソールに表示します。
import React from 'react';
import NormalDiv from './NormalDiv';
const NUM = 2 ** 8 // 表示するコンポーネント数
const TestFC: React.FC<{randomValue: number}> = ({ randomValue }) => {
return <>
{new Array(NUM).fill(null).map((__, i) => (
<NormalDiv key={i}>Hello World {randomValue}</NormalDiv>
))}
</>
}
let start: number;
const App = () => {
const [randomValue, setRandomValue] = React.useState(0);
React.useLayoutEffect(() => {
const end = new Date().getTime() - start
console.info(end)
})
return (
<React.Fragment>
<button onClick={() => {
start = new Date().getTime();
setRandomValue(Math.random());
}}>Force Rerender</button>
<TestFC randomValue={randomValue} />
</React.Fragment>
);
};
NormalDiv
コンポーネントはCSS-in-JSを使わないもの、emotionを使ったもの、styled-componentsを使ったものの3種類を用意して、レンダリングに掛かる時間をそれぞれ計測して比較します。
用意したNormalDiv
コンポーネントのコードは以下のとおりです。
import React from 'react';
const NormalDiv = (props: any) => <div {...props} />
import styled from '@emotion/styled'
const NormalDiv = styled.div``;
import styled from 'styled-components'
const NormalDiv = styled.div``;
結果
表示するコンポーネント数 | 256 | 512 | 1024 | 2048 | 4096 | 8192 |
---|---|---|---|---|---|---|
CSS-in-JS未使用 [ms] | 74 | 122 | 252 | 516 | 939 | 2028 |
emotion [ms] | 115 | 241 | 367 | 707 | 1506 | 2748 |
styled-components [ms] | 154 | 231 | 361 | 721 | 1531 | 2830 |
CSS-in-JS未使用の場合と比較してemotion, styled-componentsは +30% ~ +90% 程度レンダリングに時間がかかった。
emotionとstyled-componentsの間ではレンダリング時間に大きな差はなかった。
まとめ
InfoQの記事のとおり、たしかにCSS-in-JSを使うとレンダリングに時間がかかるようになりました。
私は名前をつけるためにconst Article = styled.div
のような変数化を多用していました。
しかし、パフォーマンスが悪化するのでなんでもかんでもstyled
を使って変数化するのは控えようと思います。