Styled-Components:最新のWeb用のCSS-in-JSライブラリ

公開: 2022-03-11

CSSは、「古いWeb」に含まれると予想されていたドキュメント用に設計されました。 SassやLessのようなプリプロセッサの出現は、コミュニティがCSSが提供する以上のものを必要としていることを示しています。 Webアプリが時間の経過とともにますます複雑になるにつれて、CSSの制限がますます明らかになり、緩和することが困難になっています。

スタイル付きコンポーネントは、完全なプログラミング言語(JavaScript)の能力とそのスコープ機能を活用して、コードをコンポーネントに構造化するのに役立ちます。 これは、大規模なプロジェクトでCSSを作成および保守する際の一般的な落とし穴を回避するのに役立ちます。 開発者は、副作用のリスクなしにコンポーネントのスタイルを説明できます。

どうしたの?

CSSを使用する利点の1つは、スタイルがコードから完全に切り離されていることです。 これは、開発者と設計者が互いに干渉することなく並行して作業できることを意味します。

一方、スタイル付きコンポーネントを使用すると、スタイルとロジックを強力に結合するという罠に陥りやすくなります。 Max Stoiberが、これを回避する方法を説明しています。 ロジックとプレゼンテーションを分離するという考えは確かに新しいものではありませんが、Reactコンポーネントを開発するときにショートカットを取りたくなるかもしれません。 たとえば、クリックアクションとボタンのスタイルを処理する検証ボタンのコンポーネントを簡単に作成できます。 2つのコンポーネントに分割するにはもう少し手間がかかります。

コンテナ/プレゼンテーションアーキテクチャ

これは非常に単純な原則です。 コンポーネントは、物事がどのように見えるかを定義するか、データとロジックを管理します。 プレゼンテーションコンポーネントの非常に重要な側面は、依存関係がないことです。 彼らは小道具を受け取り、それに応じてDOM(または子)をレンダリングします。 一方、コンテナはデータアーキテクチャ(状態、redux、fluxなど)について知っていますが、表示の責任を負わないようにする必要があります。 ダン・アブラモフの記事は、このアーキテクチャの非常に優れた詳細な説明です。

SMACSSを思い出す

CSSのスケーラブルでモジュラーなアーキテクチャはCSSを整理するためのスタイルガイドですが、基本的な概念は、ほとんどの場合、自動的にスタイル付きコンポーネントが続くものです。 アイデアは、CSSを5つのカテゴリに分類することです。

  • ベースにはすべての一般的なルールが含まれています。
  • レイアウトの目的は、構造プロパティと、コンテンツのさまざまなセクション(ヘッダー、フッター、サイドバー、コンテンツなど)を定義することです。
  • モジュールには、UIのさまざまな論理ブロックのサブカテゴリが含まれています。
  • Stateは、要素の状態を示す修飾子クラスを定義します。たとえば、エラーのあるフィールド、無効なボタンなどです。
  • テーマには、色、フォント、およびユーザーの好みに応じて変更または依存する可能性のあるその他の外観上の側面が含まれています。

スタイル付きコンポーネントを使用している間、この分離を維持するのは簡単です。 プロジェクトには通常、ある種のCSSの正規化またはリセットが含まれます。 これは通常、基本カテゴリに分類されます。 一般的なフォントサイズ、行サイズなどを定義することもできます。これは、通常のCSS(またはSass / Less)を介して、またはstyled-componentsによって提供されるinjectGlobal関数を介して行うことができます。

レイアウトルールの場合、UIフレームワークを使用すると、おそらくコンテナクラスまたはグリッドシステムが定義されます。 これらのクラスは、作成するレイアウトコンポーネントで独自のルールと組み合わせて簡単に使用できます。

スタイルは外部ファイルに記述されるのではなく、コンポーネントに直接アタッチされるため、モジュールの後にはスタイル付きコンポーネントのアーキテクチャが自動的に続きます。 基本的に、作成するスタイル付きコンポーネントはそれぞれ独自のモジュールになります。 副作用を気にせずにスタイリングコードを書くことができます。

状態は、コンポーネント内で変数ルールとして定義するルールになります。 CSS属性の値を補間する関数を定義するだけです。 UIフレームワークを使用している場合は、コンポーネントに追加するのに役立つクラスもあるかもしれません。 おそらく、CSS疑似セレクタールール(ホバー、フォーカスなど)もあります。

テーマは、コンポーネント内で簡単に補間できます。 テーマを、アプリケーション全体で使用される変数のセットとして定義することをお勧めします。 たとえば、コントラストやハイライトを処理するために、プログラムで(ライブラリを使用して、または手動で)色を導出することもできます。 あなたは自由に使えるプログラミング言語の全力を持っていることを忘れないでください!

解決策のためにそれらをまとめる

ナビゲーションを簡単にするために、これらをまとめておくことが重要です。 タイプ(プレゼンテーションとロジック)ではなく、機能別に整理したいと思います。

したがって、すべての汎用コンポーネント(ボタンなど)用のフォルダーが作成されます。 その他は、プロジェクトとその機能に応じて編成する必要があります。 たとえば、ユーザー管理機能がある場合は、その機能に固有のすべてのコンポーネントをグループ化する必要があります。

スタイル付きコンポーネントのコンテナ/プレゼンテーションアーキテクチャをSMACSSアプローチに適用するには、追加のタイプのコンポーネントである構造が必要です。 最終的には3種類のコンポーネントになります。 スタイル、構造、およびコンテナ。 styled-componentsはタグ(またはコンポーネント)を装飾するため、DOMを構造化するにはこの3番目のタイプのコンポーネントが必要です。 場合によっては、コンテナコンポーネントがサブコンポーネントの構造を処理できるようにすることもできますが、DOM構造が複雑になり、視覚的な目的で必要になる場合は、それらを分離することをお勧めします。 良い例は、DOMが通常非常に冗長になるテーブルです。

サンプルプロジェクト

これらの原則を説明するレシピを表示する小さなアプリを作成しましょう。 Recipesコンポーネントの構築を開始できます。 親コンポーネントはコントローラーになります。 状態(この場合はレシピのリスト)を処理します。 また、API関数を呼び出してデータをフェッチします。

 class Recipes extends Component{ constructor (props) { super(props); this.state = { recipes: [] }; } componentDidMount () { this.loadData() } loadData () { getRecipes().then(recipes => { this.setState({recipes}) }) } render() { let {recipes} = this.state return ( <RecipesContainer recipes={recipes} /> ) } }

レシピのリストをレンダリングしますが、その方法を知る必要はありません(また、そうすべきではありません)。 そこで、レシピのリストを取得してDOMを出力する別のコンポーネントをレンダリングします。

 class RecipesContainer extends Component{ render() { let {recipes} = this.props return ( <TilesContainer> {recipes.map(recipe => (<Recipe key={recipe.id} {...recipe}/>))} </TilesContainer> ) } }

ここでは、実際にタイルグリッドを作成します。 実際のタイルレイアウトを汎用コンポーネントにすることをお勧めします。 したがって、それを抽出すると、次のような新しいコンポーネントが得られます。

 class TilesContainer extends Component { render () { let {children} = this.props return ( <Tiles> { React.Children.map(children, (child, i) => ( <Tile key={i}> {child} </Tile> )) } </Tiles> ) } }

TilesStyles.js:

 export const Tiles = styled.div` padding: 20px 10px; display: flex; flex-direction: row; flex-wrap: wrap; ` export const Tile = styled.div` flex: 1 1 auto; ... display: flex; & > div { flex: 1 0 auto; } `

このコンポーネントは純粋に表示用であることに注意してください。 スタイルを定義し、受け取った子を、タイルがどのように見えるかを定義する別のスタイル付きDOM要素内にラップします。 これは、一般的なプレゼンテーションコンポーネントがアーキテクチャ的にどのように見えるかを示す良い例です。

次に、レシピがどのように見えるかを定義する必要があります。 比較的複雑なDOMを記述し、必要に応じてスタイルを定義するためのコンテナーコンポーネントが必要です。 最終的には次のようになります。

 class RecipeContainer extends Component { onChangeServings (e) { let {changeServings} = this.props changeServings(e.target.value) } render () { let {title, ingredients, instructions, time, servings} = this.props return ( <Recipe> <Title>{title}</Title> <div>{time}</div> <div>Serving <input type="number" min="1" max="1000" value={servings} onChange={this.onChangeServings.bind(this)}/> </div> <Ingredients> {ingredients.map((ingredient, i) => ( <Ingredient key={i} servings={servings}> <span className="name">{ingredient.name}</span> <span className="quantity">{ingredient.quantity * servings} {ingredient.unit}</span> </Ingredient> ))} </Ingredients> <div> {instructions.map((instruction, i) => (<p key={i}>{instruction}</p>))} </div> </Recipe> ) } }

ここで、コンテナはいくつかのDOM生成を行いますが、コンテナに含まれるロジックはそれだけであることに注意してください。 ネストされたスタイルを定義できるため、スタイル設定が必要なタグごとにスタイル付き要素を作成する必要がないことを忘れないでください。 これは、材料アイテムの名前と数量についてここで行うことです。 もちろん、それをさらに分割して、材料の新しいコンポーネントを作成することもできます。 プロジェクトの複雑さに応じて、粒度を決定するのはあなた次第です。 この場合、これはRecipeStylesファイルの残りの部分と一緒に定義されたスタイル付きコンポーネントです。

 export const Recipe = styled.div` background-color: ${theme('colors.background-highlight')}; `; export const Title = styled.div` font-weight: bold; ` export const Ingredients = styled.ul` margin: 5px 0; ` export const Ingredient = styled.li` & .name { ... } & .quantity { ... } `

この演習では、ThemeProviderを使用しました。 スタイリングされたコンポーネントの小道具にテーマを挿入します。 単純にcolor: ${props => props.theme.core_color} 、テーマの属性の欠落から保護するために小さなラッパーを使用しています:

 const theme = (key) => (prop) => _.get(prop.theme, key) || console.warn('missing key', key)

モジュールで独自の定数を定義し、代わりにそれらを使用することもできます。 例: color: ${styleConstants.core_color}

長所

スタイル付きコンポーネントを使用する利点は、必要なだけ使用できることです。 お気に入りのUIフレームワークを使用して、その上にスタイル付きコンポーネントを追加できます。 これは、既存のプロジェクトコンポーネントをコンポーネントごとに簡単に移行できることも意味します。 ほとんどのレイアウトを標準のCSSでスタイル設定し、再利用可能なコンポーネントにはスタイル設定されたコンポーネントのみを使用することを選択できます。

短所

デザイナー/スタイルインテグレーターは、変数を処理し、Sass/Lessの代わりにそれらを使用するための非常に基本的なJavaScriptを学ぶ必要があります。

また、プロジェクト構造をナビゲートする方法も学ぶ必要がありますが、変更する必要のあるルールを含む適切なCSS / Sass / Lessファイルを見つけるよりも、そのコンポーネントのフォルダー内のコンポーネントのスタイルを見つける方が簡単であると私は主張します。

また、構文の強調表示やリンティングなどが必要な場合は、ツールを少し変更する必要があります。開始するのに適した場所は、このAtomプラグインとこ​​のbabelプラグインです。