CSS-in-JSは、コンポーネントロジックをスタイリングにリンクする方法として、一部のコンテキストで人気になった。Aggelos Arvanitakis氏はCSS-in-JSのコストがもはや無視できない場合について開発者に注意を促し、緩和戦略を提供した。
Arvanitakis氏は記事において、CSS-in-JSがもたらす利点があるものの、一部のアプリケーションでパフォーマンス問題を引き起こす可能性があると主張した。Reactに注目すると、2つの人気のあるCSS-in-JSライブラリ(styled-componentsとemotion)があり、Arvanitakis氏は、CSS-in-JSスタイルだけの差のコードを比較した。以下はスタイルなしバージョンである:
import React from 'react';
const NormalDiv = props => <div {...props} />
const App = () => {
const [randomValue, setRandomValue] = React.useState(0);
return (
<React.Fragment>
{new Array(50).fill(null).map((__, i) => (
<NormalDiv key={i}>Hello World</NormalDiv>
))}
<button onClick={() => setRandomValue(Math.random())}>Force Rerender</button>
</React.Fragment>
);
};
スタイル付きのバージョンは:
import styled from '@emotion/styled';
const StyledDiv = styled.div``;
const App = () => {
const [randomValue, setRandomValue] = React.useState(0);
return (
<React.Fragment>
{new Array(50).fill(null).map((__, i) => (
<StyledDiv key={i}>Hello World</StyledDiv>
))}
<button onClick={() => setRandomValue(Math.random())}>Force Rerender</button>
</React.Fragment>
);
};
スタイル付けされたCSS-in-JS実装は、スタイル付けされていないバージョンと比べて50%以上もレンダリングに時間がかかるように見えた。多くの場合、CSS-in-JSに関するパフォーマンスコストはほとんど認識されていないが、(大きなコンポーネントツリー機能など)いくつかのケースでは無視できなくなる。Arvanitakis氏は、いくつかのライブラリのパフォーマンスがコストは、(スタイルの値を読み込むためにContext
と Context.Consumer
を使って) コンポーネントツリーを変更するライブラリと、(動的にCSSを挿入された<style>
タグを含む)動的にスタイルを適用するライブラリではないかと仮定した。Arvanitakis氏は説明する:
Table
を実装するまでは順調でした。50行を超えた時に、レンダリングが遅く感じることに気づきました。そのため、開発ツールを開いて調査してみました。
(…)
まとめると、複数のContext
利用者(Reactが調査する必要がある追加の要素を意味する)と、動的スタイリングに伴うハウスキーピングの組み合わせにより、アプリの速度が低下する可能性があります。
Arvanitakis氏は、次のアドバイスでまとめた:
- スタイル付けされたコンポーネントをcomposeし過ぎない
- 「静的」コンポーネントを優先する
- 不要なReact再レンダリングを避ける
- ゼロランタイムのCSS-in-JSライブラリがあなたのプロジェクトで機能するかを調査する
(…) アプリがテーマをサポートせず、css
propを重く複雑に使わない場合、ゼロランタイムCSS-in-JSが利用できる可能性がある特典として、ほとんどのCSS-in-JSライブラリは10KBから15KB程度あるが、(linariaのような)ゼロランタイムは1KBのため、全体のバンドルサイズから最大12KBを削減できる
Arvanitakis氏は、パフォーマンスのリファクタリングは、パフォーマンス問題を経験または計測した後にのみ行うべきであると警告した。 JSS CSS-in-JSライブラリの作者であるOleg Isonen氏は、4つの一般的に使われているCSS-in-JS戦略のトレードオフについて説明し、CSS-in-JSライブラリを比較したパフォーマンスベンチマーク(2019年3月時点)にリンクした。選択されたライブラリのベンチマークは以下のように実行された:
CSS-in-JSは、Reactのようなコンポーネントベースのフレームワークに限定される場合がある。Vue, Svelte, or Angularのようなそのほかの人気のフレームワークは、他のコロケーション戦略を使用して、開発者に同じようなメリット(スコープ化されたCSSとtree-shake可能なCSSなど)を提供する。Angular開発者は、HTMLのようなテンプレートファイル、CSSファイル、JavaScriptファイルでコンポーネントを定義できる。.js
ファイルは他の2つを参照する:
// ./app.component.js
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angular programming.';
}
.js
, .css
, .html
の3ファイルは、同じディレクトリに配置されることが期待されている。Angular開発者は、テンプレートとスタイルが短く、シンプルな場合、テンプレートとスタイルは@Component
定義に文字列の形で直接配置することを好む傾向にある。どちらのアプローチも構文が異なるが、同じスコープのメリットがある。
Vueは、CSSスタイル情報を含む<style>
タグ、htmlに似たテンプレート、コンポーネントの動作を処理するJavaScriptメソッドを内包する、単一の.vue
ファイルをプロモートする。同様にSvelteは、同じファイル、スタイル、テンプレート、ロジック情報を含む.svelte
ファイルのコンポーネント定義を読み込む。CSS-in-JSは、テンプレートの代わりにJavaScriptレンダリング関数を使用するReactなどのフレームワークで使われるコロケーションの別の形式である。
Elmの作成者であるEvan Czaplicki氏は、Twitterで、コンポーネントはオブジェクトであると仮定した。コロケーションは、スタイルとテンプレートプロパティと、コンポーネントロジックを処理するメソッドを一緒にまとめており、Robert C. Martin氏によって表される単一責務の原則に対応している:
単一責務の原則の他の表現は:
同じ理由で変化するものを集めます。別の理由で変化するものを分離します。
これについて考えると、凝集と結合を定義するための別の方法であることがわかります。同じ理由で変化するものの凝集を高め、異なる理由で変化するものの結合を減らしたいということです。
Arvanitakis氏の全記事には、オンラインで確認できる追加のプロファイル情報が含まれている。