ホームページ ウェブフロントエンド CSSチュートリアル すべてにコンポーネントが必要なわけではない

すべてにコンポーネントが必要なわけではない

Nov 26, 2024 am 03:32 AM

Not Everything Needs a Component

2000 年代初頭に、多くの div 要素を含む Web ページのコードを作成する行為を指す新しい用語 Divitis が作られました。意味のあるセマンティック HTML 要素の場所。これは、プログレッシブ エンハンスメント手法の枠組み内で HTML のセマンティクスに対する意識を高める取り組みの一環でした。

20 年前に遡ります - 私はウェブ開発者に影響を与える新たな症候群を目撃しました。これを私はコンポーネント炎と呼んでいます。これが私のでっち上げた定義です:

コンポーネント炎: よりシンプルで再利用可能なソリューションの代わりに、UI のあらゆる側面に対してコンポーネントを作成する実践。

コンポーネント

それでは、まず第一に、コンポーネントとは何ですか? React はその構成要素を指すこの用語を普及させたと思います:

React を使用すると、マークアップ、CSS、JavaScript をカスタムの「コンポーネント」に結合し、アプリで再利用可能な UI 要素を作成できます。
React ドキュメント - 最初のコンポーネント

再利用可能な UI 要素の概念は当時新しいものではありませんでしたが (CSS では、OOCSS、SMACSS、BEM などの技術がすでにありました)、主な違いは、マークアップ、スタイル、および交流。 React コンポーネント (および後続のすべての UI ライブラリ) を使用すると、コンポーネントの境界内の 1 つのファイルにすべてを同じ場所に配置することができます。

Facebook の最新 CSS ライブラリ Stylex を使用すると、次のように書くことができます。

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

オブジェクト記法で CSS を書くのが好きかどうかは別ですが (私はそうではありません)、このレベルのコロケーションはコンポーネントベースのプロジェクトをより保守しやすくする良い方法であることがよくあります。すべてが手の届くところにあり、明示的にバインドされています。

Svelte のようなライブラリでは、コロケーションはさらに明確になります (そしてコードはより簡潔になります)。

<script>
    let toggle = $state(false)
    const onclick = () => toggle = !toggle
</script>

<button type='button' {onclick}>
    {toggle}
</button>

<style>
button {
    font-size: 16px;
    line-height: 1.5;
    color: #000;
}
</style> 
ログイン後にコピー
ログイン後にコピー

時間が経つにつれて、このパターンは非常に注目を集め、すべてがコンポーネントにカプセル化されるまでになりました。おそらく次のようなページ コンポーネントに遭遇したことがあるでしょう:

export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <Stack spacing={2}>
                    <Item>Item 1</Item>
                    <Item>Item 2</Item>
                    <Item>Item 3</Item>
                </Stack>
            </Body>
            <Footer />
        </Layout>
    );
}
ログイン後にコピー
ログイン後にコピー

1 つのコロケーション

上記のコードはきれいで一貫性があります。ページを記述するためにコンポーネント インターフェイスを使用します。

それでは、スタックの可能な実装を見てみましょう。このコンポーネントは通常、すべての直接の子要素が垂直方向に積み重ねられ、均等な間隔で配置されるようにするためのラッパーです。

import * as stylex from "@stylexjs/stylex";
import type { PropsWithChildren } from "react";

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack({
    spacing = 0,
    children,
}: PropsWithChildren<{ spacing?: number }>) {
    return (
        <div {...stylex.props(styles.root, styles.spacing(spacing))}>
            {children}
        </div>
    );
}
ログイン後にコピー
ログイン後にコピー

スタイルとコンポーネントのルート要素のみを定義します。

この場合、HTML は CSS クラス参照を保持するためにのみ使用され、対話性やビジネスは存在しないため、同じ場所に配置しているのはスタイル ブロックだけである とも言えます。ロジック。

柔軟性の(回避可能な)コスト

それでは、ルート要素をセクションとしてレンダリングして、いくつかの属性を追加できるようにしたい場合はどうすればよいでしょうか?多態性コンポーネントの領域に入る必要があります。 React と TypeScript では、これは次のような結果になる可能性があります:

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

私の意見では、これは一見しただけではあまり読みにくいです。覚えておいてください: 3 つの CSS 宣言を含む要素をレンダリングしているだけです。

基本に戻る

少し前、私は Angular でペット プロジェクトに取り組んでいました。コンポーネントで考えることに慣れていたので、私はスタックを作成するために彼らに連絡を取りました。 Angular では、ポリモーフィック コンポーネントの作成はさらに複雑であることがわかりました。

私は自分の実装設計に疑問を抱き始めましたが、その後、ひらめきました。ソリューションはずっと目の前にあったのに、なぜ複雑な実装に時間とコード行を費やす必要があるのでしょうか?

<script>
    let toggle = $state(false)
    const onclick = () => toggle = !toggle
</script>

<button type='button' {onclick}>
    {toggle}
</button>

<style>
button {
    font-size: 16px;
    line-height: 1.5;
    color: #000;
}
</style> 
ログイン後にコピー
ログイン後にコピー

実際、これは Stack の最低限のネイティブ実装です。 CSS をレイアウトにロードすると、コード内ですぐに使用できます:

export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <Stack spacing={2}>
                    <Item>Item 1</Item>
                    <Item>Item 2</Item>
                    <Item>Item 3</Item>
                </Stack>
            </Body>
            <Footer />
        </Layout>
    );
}
ログイン後にコピー
ログイン後にコピー

JavaScript フレームワークにタイプ セーフティを追加する

CSS のみのソリューションでは、入力も IDE オートコンプリートも提供しません。

また、間隔バリアントを使用していない場合、間隔プロパティの代わりにクラスとスタイル属性の両方を記述するのは冗長すぎると感じるかもしれません。 React を使用していると仮定すると、JSX を活用してユーティリティ関数を作成できます。

import * as stylex from "@stylexjs/stylex";
import type { PropsWithChildren } from "react";

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack({
    spacing = 0,
    children,
}: PropsWithChildren<{ spacing?: number }>) {
    return (
        <div {...stylex.props(styles.root, styles.spacing(spacing))}>
            {children}
        </div>
    );
}
ログイン後にコピー
ログイン後にコピー

React TypeScript では不明な CSS プロパティが許可されていないことに注意してください。簡潔にするために型アサーションを使用しましたが、より堅牢なソリューションを選択する必要があります。

バリアントを使用している場合は、ユーティリティ関数を変更して、PandaCSS パターンと同様の開発者エクスペリエンスを提供できます。

import * as stylex from "@stylexjs/stylex";

type PolymorphicComponentProps<T extends React.ElementType> = {
    as?: T;
    children?: React.ReactNode;
    spacing?: number;
} & React.ComponentPropsWithoutRef<T>;

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack<T extends React.ElementType = "div">({
    as,
    spacing = 1,
    children,
    ...props
}: PolymorphicComponentProps<T>) {
    const Component = as || "div";
    return (
        <Component
            {...props}
            {...stylex.props(styles.root, styles.spacing(spacing))}
        >
            {children}
        </Component>
    );
}
ログイン後にコピー

コードの重複とハードコードされた値を防止する

最後の例で、CSS とユーティリティ ファイルの両方に期待される間隔の値をハードコーディングしたことに気付いた方もいるかもしれません。値が削除または追加された場合、2 つのファイルの同期を維持する必要があるため、これが問題になる可能性があります。

ライブラリを構築している場合、自動化されたビジュアル回帰テストでこの種の問題が検出される可能性があります。とにかく、それでも気になる場合は、CSS モジュールに手を伸ばし、typed-css-modules を使用するか、サポートされていない値に対してランタイム エラーをスローすることが解決策になるかもしれません:

<div>





<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
    display: flex;
    flex-direction: column;
    row-gap: calc(var(--s) * 16px);
}
ログイン後にコピー
export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <div className="stack">



<p>Let's see the main advantages of this approach:</p>

<ul>
<li>reusability</li>
<li>reduced complexity</li>
<li>smaller JavaScript bundle and less overhead</li>
<li><strong>interoperability</strong></li>
</ul>

<p>The last point is easy to overlook: Not every project uses React, and if you’re including the stack layout pattern in a Design System or a redistributable UI library, developers could use it in projects using different UI frameworks or a server-side language like PHP or Ruby.</p>

<h2>
  
  
  Nice features and improvements
</h2>

<p>From this base, you can iterate to add more features and improve the developer experience. While some of the following examples target React specifically, they can be easily adapted to other frameworks.</p>

<h3>
  
  
  Control spacing
</h3>

<p>If you're developing a component library you definitely want to define a set of pre-defined spacing variants to make space more consistent. This approach also eliminates the need to explicitly write the style attribute:<br>
</p>

<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
  display: flex;
  flex-direction: column;
  row-gap: calc(var(--s) * 16px);

  &.s\:1 { --s: 1 }
  &.s\:2 { --s: 2 }
  &.s\:4 { --s: 4 }
  &.s\:6 { --s: 6 }
}

/** Usage:
<div>



<p>For a bolder approach to spacing, see Complementary Space by Donnie D'Amato.</p>

<h3>
  
  
  Add better scoping
</h3>

<p>Scoping, in this case, refers to techniques to prevent conflicts with other styles using the same selector. I’d argue that scoping issues affects a pretty small number of projects, but if you are really concerned about it, you could:</p>

<ol>
<li>Use something as simple as CSS Modules, which is well supported in all major bundlers and frontend frameworks.</li>
<li>Use cascade layers resets to prevent external stylesheets from modifying your styles (this is an interesting technique).</li>
<li>Define a specific namespace like .my-app-... for your classes.</li>
</ol>

<p>Here is the result with CSS Modules:<br>
</p>

<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
  display: flex;
  flex-direction: column;
  row-gap: calc(var(--s) * 16px);

  &.s1 { --s: 1 }
  &.s2 { --s: 2 }
  &.s4 { --s: 4 }
  &.s6 { --s: 6 }
}

/** Usage
import * from './styles/stack.module.css'

<div className={`${styles.stack} ${styles.s2}`}>
    // ...
</div>  
*/
ログイン後にコピー

代替案

それでもポリモーフィックコンポーネントの方が良いと思う、プレーンな HTML を扱うことができない、または CSS を別のファイルに書きたくない (理由はわかりませんが) 場合、私の次の提案は次のとおりです。 PandaCSS を見てカスタム パターンを作成するか、vanilla-extract などの他のオプションを検討してください。私の意見では、これらのツールは過剰に設計された CSS メタ言語ですが、それでもポリモーフィック コンポーネントよりは優れています。

検討する価値のあるもう 1 つの代替案は、Tailwind CSS です。これには、言語とフレームワーク間で相互運用できるという利点があります

Tailwind によって定義されたデフォルトの間隔スケールを使用して、次のようなスタック プラグインを作成できます。

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

余談ですが、実際のコンポーネントを作成しない場合でも、Tailwind が複雑な CSS ルールセットを記述するために matchComponents のコンポーネント メンタル モデルを使用していることは興味深いです。この概念がどれほど浸透しているかを示すもう 1 つの例でしょうか?

テイクアウト

コンポーネント炎の事例は、その技術的な側面を超えて、私たちのメンタルモデルと習慣を調べ、疑問を抱くために立ち止まることの重要性を示しています。ソフトウェア開発の多くのパターンと同様、コンポーネントは実際の問題に対する解決策として登場しましたが、このパターンをデフォルトにし始めると、それが複雑さの隠れた原因となりました。 成分炎は、食事制限によって引き起こされる栄養欠乏症に似ています。問題は、特定の食品にあるのではなく、他のすべてを欠いていることにあります。

以上がすべてにコンポーネントが必要なわけではないの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

静的フォームプロバイダーの比較 静的フォームプロバイダーの比較 Apr 16, 2025 am 11:20 AM

ここでは、「静的フォームプロバイダー」という用語を埋めてみましょう。あなたはあなたのHTMLを持ってきます

SASSをより速くするための概念の証明 SASSをより速くするための概念の証明 Apr 16, 2025 am 10:38 AM

新しいプロジェクトの開始時に、SASSコンピレーションは瞬く間に起こります。これは、特にbrowsersyncとペアになっている場合は素晴らしい気分です。

毎週のプラットフォームニュース:HTMLロード属性、主なARIA仕様、およびIFRAMEからShadowDOMへの移動 毎週のプラットフォームニュース:HTMLロード属性、主なARIA仕様、およびIFRAMEからShadowDOMへの移動 Apr 17, 2025 am 10:55 AM

今週のプラットフォームニュースのラウンドアップで、Chromeは、Web開発者のロード、アクセシビリティ仕様、およびBBCの動きのための新しい属性を導入します

HTMLダイアログ要素を使用したいくつかの実践 HTMLダイアログ要素を使用したいくつかの実践 Apr 16, 2025 am 11:33 AM

これは私が初めてHTML要素を見ていることです。私はしばらくの間それを知っていましたが、まだスピンしていませんでした。かなりクールです

ペーパーフォーム ペーパーフォーム Apr 16, 2025 am 11:24 AM

購入またはビルドは、テクノロジーの古典的な議論です。自分で物を構築することは、あなたのクレジットカードの請求書にはラインアイテムがないため、安価に感じるかもしれませんが

毎週のプラットフォームニュース:テキスト間隔のブックマークレット、トップレベルの待望、新しいアンプロードインジケーター 毎週のプラットフォームニュース:テキスト間隔のブックマークレット、トップレベルの待望、新しいアンプロードインジケーター Apr 17, 2025 am 11:26 AM

今週のラウンドアップ、タイポグラフィを検査するための便利なブックマークレットである。

「ポッドキャストにサブスクライブ」リンクはどこにすべきですか? 「ポッドキャストにサブスクライブ」リンクはどこにすべきですか? Apr 16, 2025 pm 12:04 PM

しばらくの間、iTunesはポッドキャストの大きな犬だったので、「ポッドキャストにサブスクライブ」をリンクした場合:

独自の非JavaScriptベースの分析をホストするためのオプション 独自の非JavaScriptベースの分析をホストするためのオプション Apr 15, 2025 am 11:09 AM

サイトの訪問者と使用データを追跡するのに役立つ分析プラットフォームがたくさんあります。おそらく、特にGoogleアナリティクスが広く使用されています

See all articles