Heim > Web-Frontend > CSS-Tutorial > Hauptteil

Nicht alles braucht eine Komponente

DDD
Freigeben: 2024-11-26 03:32:12
Original
203 Leute haben es durchsucht

Not Everything Needs a Component

Anfang der 2000er Jahre wurde ein neuer Begriff, Divitis, geprägt, der sich auf Die Praxis des Verfassens von Webseitencode mit vielen div-Elementen bezieht Ort sinnvoller semantischer HTML-Elemente. Dies war Teil der Bemühungen, das Bewusstsein für die Semantik in HTML im Rahmen der Progressive Enhancement-Technik zu schärfen.

20 Jahre später bin ich Zeuge eines neuen Syndroms, das Webentwickler betrifft und das ich Komponentitis nenne. Hier ist meine erfundene Definition:

Componentitis: die Praxis, eine Komponente für jeden Aspekt einer Benutzeroberfläche anstelle einer einfacheren und wiederverwendbareren Lösung zu erstellen.

Komponenten

Also zunächst einmal: Was ist eine Komponente? Ich denke, React hat den Begriff populär gemacht, um sich auf seine Bausteine ​​zu beziehen:

Mit React können Sie Ihr Markup, CSS und JavaScript zu benutzerdefinierten „Komponenten“ kombinieren, wiederverwendbaren UI-Elementen für Ihre App.
React-Dokumentation – Ihre erste Komponente

Während das Konzept der wiederverwendbaren UI-Elemente damals nicht neu war (in CSS gab es bereits Techniken wie OOCSS, SMACSS und BEM), liegt der Hauptunterschied in der ursprünglichen Herangehensweise an die Position von Markup, Stil und Interaktion. Mit React-Komponenten (und allen nachfolgenden UI-Bibliotheken) ist es möglich, alles in einer einzigen Datei innerhalb der Grenzen einer Komponente zusammenzulokalisieren.

Mit der neuesten CSS-Bibliothek Stylex von Facebook könnten Sie also schreiben:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Sie können ein Fan davon sein oder nicht, CSS in Objektnotation zu schreiben (ich bin es nicht), aber diese Ebene der gemeinsamen Anordnung ist oft eine gute Möglichkeit, ein komponentenbasiertes Projekt wartbarer zu machen: Alles ist in Reichweite und ausdrücklich gebunden.

In Bibliotheken wie Svelte ist die Co-Location noch klarer (und der Code prägnanter):

<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> 
Nach dem Login kopieren
Nach dem Login kopieren

Im Laufe der Zeit hat dieses Muster so viel Anklang gefunden, dass alles in Komponenten gekapselt ist. Sie sind wahrscheinlich auf Seitenkomponenten wie diese gestoßen:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren

Co-Location von einem

Der obige Code sieht sauber und konsistent aus: Wir verwenden die Komponentenschnittstelle, um eine Seite zu beschreiben.

Aber dann schauen wir uns die mögliche Implementierung von Stack an. Diese Komponente ist normalerweise ein Wrapper, um sicherzustellen, dass alle direkten untergeordneten Elemente vertikal gestapelt und gleichmäßig verteilt sind:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren

Wir definieren nur die Stile und das Stammelement der Komponente.

In diesem Fall könnten wir sogar sagen, dass das Einzige, was wir gemeinsam platzieren, der Stilblock ist, da der HTML-Code nur zum Speichern einer CSS-Klassenreferenz verwendet wird und es keine Interaktivität oder Geschäftlichkeit gibt Logik.

Die (vermeidbaren) Kosten der Flexibilität

Was ist nun, wenn wir das Stammelement als Abschnitt rendern und möglicherweise einige Attribute hinzufügen möchten? Wir müssen in den Bereich der polymorphen Komponenten vordringen. In React und mit TypeScript könnte dies am Ende etwa so aussehen:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Meiner Meinung nach ist das auf den ersten Blick nicht sehr lesbar. Und denken Sie daran: Wir rendern lediglich ein Element mit 3 CSS-Deklarationen.

Zurück zum Wesentlichen

Vor einiger Zeit arbeitete ich an einem Lieblingsprojekt in Angular. Da ich es gewohnt war, in Komponenten zu denken, wandte ich mich an sie, um einen Stack zu erstellen. Es stellt sich heraus, dass die Erstellung polymorpher Komponenten in Angular noch komplexer ist.

Ich fing an, meinen Implementierungsentwurf in Frage zu stellen, und dann hatte ich eine Offenbarung: Warum sollte ich Zeit und Codezeilen für komplexe Implementierungen aufwenden, wenn die Lösung die ganze Zeit direkt vor mir lag?

<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> 
Nach dem Login kopieren
Nach dem Login kopieren

Wirklich, das ist die grundlegende native Implementierung des Stacks. Sobald Sie das CSS im Layout geladen haben, kann es sofort in Ihrem Code verwendet werden:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren

Fügen Sie Typsicherheit in JavaScript-Frameworks hinzu

Die reine CSS-Lösung bietet weder Eingabe noch IDE-Autovervollständigung.

Wenn wir außerdem keine Abstandsvarianten verwenden, könnte es sich zu ausführlich anfühlen, sowohl ein Klassen- als auch ein Stilattribut anstelle einer Abstandsstütze zu schreiben. Vorausgesetzt, Sie verwenden React, könnten Sie JSX nutzen und eine Hilfsfunktion erstellen:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren

Beachten Sie, dass React TypeScript keine unbekannten CSS-Eigenschaften zulässt. Der Kürze halber habe ich eine Typzusicherung verwendet, aber Sie sollten eine robustere Lösung wählen.

Wenn Sie Varianten verwenden, können Sie die Dienstprogrammfunktion ändern, um eine Entwicklererfahrung ähnlich den PandaCSS-Mustern bereitzustellen:

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>
    );
}
Nach dem Login kopieren

Verhindern Sie Codeduplizierung und fest codierte Werte

Einige von Ihnen haben vielleicht bemerkt, dass ich im letzten Beispiel die erwarteten Abstandswerte sowohl im CSS als auch in den Dienstprogrammdateien fest codiert habe. Wenn ein Wert entfernt oder hinzugefügt wird, kann dies ein Problem darstellen, da wir die beiden Dateien synchron halten müssen.

Wenn Sie eine Bibliothek erstellen, werden automatisierte visuelle Regressionstests diese Art von Problem wahrscheinlich erkennen. Wie auch immer, wenn es Sie immer noch stört, könnte eine Lösung darin bestehen, nach CSS-Modulen zu greifen und entweder typisierte CSS-Module zu verwenden oder einen Laufzeitfehler für nicht unterstützte Werte auszulösen:

<div>





<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
    display: flex;
    flex-direction: column;
    row-gap: calc(var(--s) * 16px);
}
Nach dem Login kopieren
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>  
*/
Nach dem Login kopieren

Alternativen

Wenn Sie immer noch der Meinung sind, dass eine polymorphe Komponente besser wäre, Sie wirklich nicht mit einfachem HTML umgehen können oder CSS nicht in eine separate Datei schreiben möchten (obwohl ich nicht sicher bin, warum), wäre mein nächster Vorschlag, dies zu tun Werfen Sie einen Blick auf PandaCSS und erstellen Sie benutzerdefinierte Muster oder erkunden Sie andere Optionen wie Vanilla-Extract. Meiner Meinung nach sind diese Tools eine überentwickelte CSS-Metasprache, aber immer noch besser als eine polymorphe Komponente.

Eine weitere erwägenswerte Alternative ist Tailwind CSS, das den Vorteil hat, dass es zwischen Sprachen und Frameworks interoperabel ist.

Unter Verwendung der von Tailwind definierten Standardabstandsskala könnten wir ein Stack-Plugin wie dieses erstellen:

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>
    );
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Als Randbemerkung: Es ist interessant, dass Tailwind das mentale Komponentenmodell in matchComponents verwendet, um komplexe CSS-Regelsätze zu beschreiben, auch wenn dadurch keine echte Komponente erstellt wird. Vielleicht ein weiteres Beispiel dafür, wie allgegenwärtig das Konzept ist?

Imbissbuden

Der Fall der Componentitis zeigt über die technischen Aspekte hinaus, wie wichtig es ist, innezuhalten, um unsere mentalen Modelle und Gewohnheiten zu untersuchen und zu hinterfragen. Wie viele Muster in der Softwareentwicklung entstanden Komponenten als Lösungen für reale Probleme, aber als wir begannen, standardmäßig auf dieses Muster zurückzugreifen, wurde es zu einer stillen Quelle der Komplexität. Komponentitis ähnelt den Nährstoffmängeln, die durch eine eingeschränkte Ernährung verursacht werden: Das Problem liegt nicht an einem einzelnen Lebensmittel, sondern darin, dass alles andere fehlt.

Das obige ist der detaillierte Inhalt vonNicht alles braucht eine Komponente. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage