mirror of
https://github.com/vercel/components.build.git
synced 2026-09-14 20:06:39 +08:00
55fae375a4
* Update Geistdocs * Update geistdocs.tsx * Run translation script * Delete toc.tsx * Update route.ts * Update route.ts
233 lines
7.4 KiB
Plaintext
233 lines
7.4 KiB
Plaintext
---
|
|
title: Composition
|
|
description: Die Grundlage für den Aufbau moderner UI-Komponenten.
|
|
---
|
|
|
|
Composition, oder Komponierbarkeit, ist die Grundlage für den Aufbau moderner UI-Komponenten. Sie ist eine der mächtigsten Techniken, um flexible, wiederverwendbare Komponenten zu erstellen, die komplexe Anforderungen erfüllen können, ohne die Klarheit der API zu opfern.
|
|
|
|
Anstatt alle Funktionen in eine einzelne Komponente mit Dutzenden von Props zu quetschen, verteilt Composition die Verantwortung auf mehrere zusammenarbeitende Komponenten.
|
|
|
|
Fernando hielt dazu einen großartigen Vortrag auf der React Universe Conf 2025, in dem er seinen Ansatz zum Wiederaufbau von Slack's Message Composer als komponierbare Komponente vorstellte.
|
|
|
|
<Video src="https://www.youtube.com/watch?v=4KvbVq3Eg5w" />
|
|
|
|
## Eine Komponente komponierbar machen
|
|
|
|
Um eine Komponente komponierbar zu machen, müssen Sie sie in kleinere, fokussiertere Komponenten aufteilen. Betrachten wir zum Beispiel diese Accordion-Komponente:
|
|
|
|
```tsx title="accordion.tsx"
|
|
import { Accordion } from '@/components/ui/accordion';
|
|
|
|
const data = [
|
|
{
|
|
title: 'Accordion 1',
|
|
content: 'Accordion 1 content',
|
|
},
|
|
{
|
|
title: 'Accordion 2',
|
|
content: 'Accordion 2 content',
|
|
},
|
|
{
|
|
title: 'Accordion 3',
|
|
content: 'Accordion 3 content',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Accordion data={data} />
|
|
);
|
|
```
|
|
|
|
Obwohl diese Accordion-Komponente einfach erscheinen mag, übernimmt sie zu viele Verantwortlichkeiten. Sie ist zuständig für das Rendern des Containers, des Triggers und des Inhalts sowie für die Verwaltung des Accordion-Zustands und der Daten.
|
|
|
|
Die Anpassung des Stylings dieser Komponente ist schwierig, weil sie eng gekoppelt ist. Wahrscheinlich sind globale CSS-Überschreibungen erforderlich. Außerdem erfordert das Hinzufügen neuer Funktionen oder das Anpassen des Verhaltens Änderungen am Quellcode der Komponente.
|
|
|
|
Um das zu lösen, können wir dies in kleinere, fokussiertere Komponenten aufteilen.
|
|
|
|
### 1. Root-Komponente
|
|
|
|
Zuerst konzentrieren wir uns auf den Container — die Komponente, die alles zusammenhält, also Trigger und Content. Dieser Container muss nichts über die Daten wissen, muss jedoch den geöffneten Zustand verfolgen.
|
|
|
|
Wir möchten diesen Zustand jedoch auch für Kindkomponenten zugänglich machen. Verwenden wir also die Context API, um einen Kontext für den geöffneten Zustand zu erstellen.
|
|
|
|
Schließlich erweitern wir die Standard-HTML-Attribute, um die Modifikation des `div`-Elements zu ermöglichen.
|
|
|
|
Wir nennen diese Komponente die "Root"-Komponente.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
type AccordionProps = React.ComponentProps<'div'> & {
|
|
open: boolean;
|
|
setOpen: (open: boolean) => void;
|
|
};
|
|
|
|
const AccordionContext = createContext<AccordionProps>({
|
|
open: false,
|
|
setOpen: () => {},
|
|
});
|
|
|
|
export type AccordionRootProps = React.ComponentProps<'div'> & {
|
|
open: boolean;
|
|
setOpen: (open: boolean) => void;
|
|
};
|
|
|
|
export const Root = ({
|
|
children,
|
|
open,
|
|
setOpen,
|
|
...props
|
|
}: AccordionRootProps) => (
|
|
<AccordionContext.Provider value={{ open, setOpen }}>
|
|
<div {...props}>{children}</div>
|
|
</AccordionContext.Provider>
|
|
);
|
|
```
|
|
|
|
### 2. Item-Komponente
|
|
|
|
Die Item-Komponente ist das Element, das den Eintrag im Accordion enthält. Sie ist einfach ein Wrapper für jeden Eintrag im Accordion.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionItemProps = React.ComponentProps<'div'>;
|
|
|
|
export const Item = (props: AccordionItemProps) => <div {...props} />;
|
|
```
|
|
|
|
### 3. Trigger-Komponente
|
|
|
|
Die Trigger-Komponente ist das Element, das das Accordion beim Aktivieren öffnet. Sie ist verantwortlich für:
|
|
|
|
- Standardmäßig als Button zu rendern (kann mit `asChild` angepasst werden)
|
|
- Klickereignisse zu verarbeiten, um das Accordion zu öffnen
|
|
- Den Fokus zu verwalten, wenn das Accordion schließt
|
|
- Bereitstellung geeigneter ARIA-Attribute
|
|
|
|
Fügen wir diese Komponente zu unserer Accordion-Komponente hinzu.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionTriggerProps = React.ComponentProps<'button'> & {
|
|
asChild?: boolean;
|
|
};
|
|
|
|
export const Trigger = ({ asChild, ...props }: AccordionTriggerProps) => (
|
|
<AccordionContext.Consumer>
|
|
{({ open, setOpen }) => (
|
|
<button onClick={() => setOpen(!open)} {...props} />
|
|
)}
|
|
</AccordionContext.Consumer>
|
|
);
|
|
```
|
|
|
|
### 4. Content-Komponente
|
|
|
|
Die Content-Komponente ist das Element, das den Accordion-Inhalt enthält. Sie ist verantwortlich für:
|
|
|
|
- Das Rendern des Inhalts, wenn das Accordion geöffnet ist
|
|
- Bereitstellung geeigneter ARIA-Attribute
|
|
|
|
Fügen wir diese Komponente zu unserer Accordion-Komponente hinzu.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionContentProps = React.ComponentProps<'div'> & {
|
|
asChild?: boolean;
|
|
};
|
|
|
|
export const Content = ({ asChild, ...props }: AccordionContentProps) => (
|
|
<AccordionContext.Consumer>
|
|
{({ open }) => <div {...props} />}
|
|
</AccordionContext.Consumer>
|
|
);
|
|
```
|
|
|
|
### 5. Alles zusammenfügen
|
|
|
|
Nachdem wir nun alle Komponenten haben, können wir sie in unserer ursprünglichen Datei zusammenfügen.
|
|
|
|
```tsx title="accordion.tsx"
|
|
import * as Accordion from '@/components/ui/accordion';
|
|
|
|
const data = [
|
|
{
|
|
title: 'Accordion 1',
|
|
content: 'Accordion 1 content',
|
|
},
|
|
{
|
|
title: 'Accordion 2',
|
|
content: 'Accordion 2 content',
|
|
},
|
|
{
|
|
title: 'Accordion 3',
|
|
content: 'Accordion 3 content',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Accordion.Root open={false} setOpen={() => {}}>
|
|
{data.map((item) => (
|
|
<Accordion.Item key={item.title}>
|
|
<Accordion.Trigger>{item.title}</Accordion.Trigger>
|
|
<Accordion.Content>{item.content}</Accordion.Content>
|
|
</Accordion.Item>
|
|
))}
|
|
</Accordion.Root>
|
|
);
|
|
```
|
|
|
|
## Namenskonventionen
|
|
|
|
Beim Erstellen komponierbarer Komponenten sind konsistente Namenskonventionen entscheidend, um intuitive und vorhersehbare APIs zu schaffen. Sowohl shadcn/ui als auch Radix UI folgen etablierten Mustern, die zum De-facto-Standard im React-Ökosystem geworden sind.
|
|
|
|
### Root-Komponenten
|
|
|
|
Die `Root`-Komponente dient als Hauptcontainer, der alle anderen Sub-Komponenten umschließt. Sie verwaltet typischerweise gemeinsamen Zustand und Context, indem sie einen Kontext für alle Kindkomponenten bereitstellt.
|
|
|
|
```tsx
|
|
<AccordionRoot>{/* Child components */}</AccordionRoot>
|
|
```
|
|
|
|
### Interaktive Elemente
|
|
|
|
Interaktive Komponenten, die Aktionen auslösen oder Zustände umschalten, verwenden beschreibende Namen:
|
|
|
|
- `Trigger` - Das Element, das eine Aktion auslöst (Öffnen, Schließen, Umschalten)
|
|
- `Content` - Das Element, das den Hauptinhalt enthält, der angezeigt/ausgeblendet wird
|
|
|
|
```tsx
|
|
<CollapsibleTrigger>Click to expand</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
Hidden content revealed here
|
|
</CollapsibleContent>
|
|
```
|
|
|
|
### Inhaltsstruktur
|
|
|
|
Für Komponenten mit strukturierten Inhaltsbereichen verwenden Sie semantische Namen, die ihren Zweck beschreiben:
|
|
|
|
- `Header` - Oberer Abschnitt mit Titeln oder Steuerungen
|
|
- `Body` - Hauptinhaltbereich
|
|
- `Footer` - Unterer Abschnitt für Aktionen oder Metadaten
|
|
|
|
```tsx
|
|
<DialogHeader>
|
|
{/* Dialog title */}
|
|
</DialogHeader>
|
|
<DialogBody>
|
|
{/* Dialog content */}
|
|
</DialogBody>
|
|
<DialogFooter>
|
|
{/* Dialog footer */}
|
|
</DialogFooter>
|
|
```
|
|
|
|
### Informationskomponenten
|
|
|
|
Komponenten, die Informationen oder Kontext bereitstellen, verwenden beschreibende Suffixe:
|
|
|
|
- `Title` - Hauptüberschrift oder Label
|
|
- `Description` - Unterstützender Text oder erläuternder Inhalt
|
|
|
|
```tsx
|
|
<CardTitle>Project Statistics</CardTitle>
|
|
<CardDescription>
|
|
View your project's performance over time
|
|
</CardDescription>
|
|
``` |