用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Dev-Toolbelt/dev-team-agents --skill component-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | component-patterns |
| description | Component patterns — container/presentational for React/Vue/Angular. |
The core idea: separate what data comes from (container) from how it looks (presentational). Presentational components receive everything via props/inputs — they own no side effects and no data fetching.
| Aspect | Container (Smart) | Presentational (Dumb) |
|---|---|---|
| Responsibility | Fetch data, manage state, handle business logic | Render UI, emit user events |
| Dependencies | API clients, stores, router | None beyond UI primitives |
| Reusability | Low — tied to a specific domain | High — usable anywhere |
| Testability | Test behavior + integration | Test with prop snapshots / storybook |
| State | Owns or subscribes to state | Receives everything via props/inputs |
When to split: as soon as a component both fetches data AND renders UI, extract the rendering into a presentational child. This makes the UI testable without mocking the network.
features/orders must not import directly from features/users; go through a shared layer.// Container — owns data fetching and state
function OrderListContainer() {
const { data, isLoading, error } = useOrders()
if (isLoading) return <Spinner />
if (error) return <ErrorMessage error={error} />
return <OrderList orders={data} />
}
// Presentational — pure render, fully testable with props alone
function OrderList({ orders }: { orders: Order[] }) {
return (
<ul>
{orders.map(o => <OrderItem key={o.id} order={o} />)}
</ul>
)
}
React-specific rules:
useOrders() over class-based containers.ErrorBoundary; presentational components should not catch errors themselves.React.memo on stable presentational components to avoid unnecessary re-renders from parent state changes.<!-- Container -->
<script setup lang="ts">
const { orders, isLoading, error } = useOrders()
</script>
<template>
<Spinner v-if="isLoading" />
<ErrorMessage v-else-if="error" :error="error" />
<OrderList v-else :orders="orders" />
</template>
<!-- Presentational -->
<script setup lang="ts">
defineProps<{ orders: Order[] }>()
</script>
<template>
<ul>
<OrderItem v-for="o in orders" :key="o.id" :order="o" />
</ul>
</template>
Vue-specific rules:
useX) are the Vue equivalent of React hooks — put all fetching and state logic there.defineProps with explicit types; avoid prop drilling beyond two levels — use provide/inject or a store.defineEmits and typed payloads; never mutate a prop directly.// Container — smart component, injects service
@Component({
selector: 'app-order-list-container',
template: `
<app-spinner *ngIf="isLoading" />
<app-error-message *ngIf="error" [error]="error" />
<app-order-list *ngIf="!isLoading && !error" [orders]="orders" />
`
})
export class OrderListContainerComponent implements OnInit {
orders: Order[] = []
isLoading = false
error: Error | null = null
constructor(private orderService: OrderService) {}
ngOnInit() {
this.isLoading = true
this.orderService.getOrders().subscribe({
next: orders => { this.orders = orders; this.isLoading = false },
error: err => { this.error = err; this.isLoading = false }
})
}
}
// Presentational — dumb component, only @Input / @Output
({
: ,
:
})
{
() : [] = []
}
Angular-specific rules:
@Input and @Output.OnPush change detection on presentational components — they receive immutable inputs, so there's no need for the default dirty-check cycle.ErrorHandler and is provided at the root level ({ provide: ErrorHandler, useClass: AppErrorHandler }); do not swallow errors silently.async pipe over manual subscriptions to avoid memory leaks from forgotten unsubscribe() calls.<!-- Container -->
<script lang="ts">
import { onMount } from 'svelte'
let orders: Order[] = []
let isLoading = true
onMount(async () => {
orders = await fetchOrders()
isLoading = false
})
</script>
{#if isLoading}<Spinner />{:else}<OrderList {orders} />{/if}
<!-- Presentational -->
<script lang="ts">
export let orders: Order[]
</script>
<ul>{#each orders as o}<OrderItem order={o} />{/each}</ul>
Svelte-specific rules:
$: reactive declarations that reach outside the component boundary.writable, derived) as the shared-state layer rather than passing deeply nested props.<svelte:boundary> (Svelte 5) for error isolation.| Anti-Pattern | Problem |
|---|---|
| Fetching inside a presentational component | Makes the component untestable without network mocking |
| Business logic in template expressions | Hard to test, hard to read |
| Prop drilling beyond two levels | Use a store, context, or composable instead |
| God component | One component handling routing, fetching, form state, and rendering |
| Direct store mutation in presentational components | Breaks the unidirectional data flow contract |