Skip to Content

27b - 组件生成策略

本文是《AI Agent 实战手册》第 27 章第 2 节。 上一节:27a-AI辅助前端开发概览 | 下一节:27c-设计到代码工作流

概述

组件是现代前端开发的基本构建单元,也是 AI 代码生成最擅长的领域。本节系统化地覆盖 React、Vue、Svelte 三大框架的组件生成策略,包括函数组件、Hooks/Composables/Runes、状态管理和样式方案的 Prompt 模式,帮助开发者建立一套可复用的 AI 组件生成方法论。无论你使用哪个框架,都能找到对应的最佳实践和即用型提示词模板。


1. 组件生成基础理论

1.1 为什么组件是 AI 生成的最佳粒度

前端组件具有几个特征,使其成为 AI 代码生成的理想目标:

特征说明对 AI 生成的影响
自包含性组件封装了模板、逻辑和样式AI 可以在一次生成中产出完整可用的代码单元
明确的接口Props/Events 定义了组件的输入输出AI 可以从接口定义推断实现细节
可视化验证组件渲染为可见的 UI开发者可以立即判断生成质量
丰富的训练数据GitHub 上有海量开源组件AI 对常见组件模式有深度理解
Token 友好单个组件通常在 200-500 行以内不超出 AI 的有效生成窗口

1.2 组件生成的四层模型

AI 组件生成可以分为四个层次,每层对应不同的 Prompt 策略:

┌─────────────────────────────────────────────────┐ │ 第四层:组件组合与页面组装 │ │ 多个组件协同工作,数据流、路由、布局 │ ├─────────────────────────────────────────────────┤ │ 第三层:状态管理与数据流 │ │ 全局状态、服务端状态、组件间通信 │ ├─────────────────────────────────────────────────┤ │ 第二层:交互逻辑与副作用 │ │ 事件处理、API 调用、动画、表单验证 │ ├─────────────────────────────────────────────────┤ │ 第一层:UI 结构与样式 │ │ HTML 结构、CSS 样式、响应式布局、无障碍 │ └─────────────────────────────────────────────────┘

关键原则:从底层向上逐层生成,每层验证后再进入下一层。不要试图让 AI 一次生成包含复杂状态管理和多组件交互的完整页面。

1.3 组件生成工具选择矩阵

工具最佳场景支持框架价格AI 生成质量
v0.devUI 组件原型、shadcn/ui 风格React免费(5 credits/天)/ $20/月⭐⭐⭐⭐⭐
Cursor Composer多文件组件开发、重构React/Vue/Svelte/Angular$20/月(Pro)⭐⭐⭐⭐⭐
Claude Code复杂组件、全项目理解React/Vue/Svelte/Angular$20/月(Max 5x)⭐⭐⭐⭐⭐
KiroSpec 驱动组件开发React/Vue/Svelte免费(预览期)⭐⭐⭐⭐
Bolt.new快速全栈原型React/Vue/Svelte免费 / $20/月⭐⭐⭐⭐
GitHub Copilot行级补全、内联生成所有框架$10/月⭐⭐⭐⭐
Windsurf类 Cursor 体验、预算友好React/Vue/Svelte免费 / $15/月⭐⭐⭐⭐

1.4 通用 Prompt 结构框架

无论使用哪个框架,高质量的组件生成 Prompt 都应包含以下结构:

## 组件:[组件名称] ### 1. 功能描述 [2-3 句话描述组件用途] ### 2. 技术约束 - 框架:[React/Vue/Svelte] + [版本] - 语言:TypeScript - 样式:[Tailwind/CSS Modules/styled-components] - 组件库:[shadcn/ui/Vuetify/Skeleton UI/无] ### 3. Props 接口 [列出所有 props 及其类型] ### 4. 状态需求 [描述内部状态和外部状态依赖] ### 5. 交互行为 [描述用户交互和状态变化] ### 6. 视觉规范 [尺寸、颜色、间距、响应式行为] ### 7. 无障碍要求 [ARIA 角色、键盘导航、屏幕阅读器支持] ### 8. 测试要求 [需要覆盖的测试场景]

2. React 组件生成策略

2.1 React 组件生成的优势与挑战

React 是 AI 代码生成支持最好的前端框架,原因在于:

优势:

  • 训练数据最丰富——GitHub 上 React 项目数量远超其他框架
  • 函数组件 + JSX 的模式简单直观,AI 生成准确率高
  • shadcn/ui + Tailwind CSS 生态成熟,AI 工具原生支持
  • TypeScript 类型系统帮助 AI 生成更准确的代码

挑战:

  • Hooks 规则(不能在条件语句中调用)AI 偶尔违反
  • Server Component vs Client Component 边界容易混淆
  • 闭包陷阱(stale closure)在复杂状态逻辑中出现
  • 过度使用 useEffect 是 AI 生成代码的常见问题

2.2 函数组件生成模式

基础函数组件 Prompt

生成一个 React 函数组件 [ComponentName]: ## 技术栈 - React 19 + TypeScript - Tailwind CSS 4 - shadcn/ui 组件库 ## Props 接口 interface [ComponentName]Props { [propName]: [type]; // [描述] [propName]?: [type]; // [描述](可选,默认值:[值]) } ## 功能描述 [详细描述组件功能] ## 变体 - size: "sm" | "md" | "lg" - variant: "[variant1]" | "[variant2]" ## 要求 1. 使用 named export 2. Props 使用解构 + 默认值 3. 使用 cn() 工具函数合并 className 4. 支持 className prop 透传 5. 使用 forwardRef 支持 ref 转发 6. 包含 displayName 设置

实际示例:生成 UserCard 组件

生成一个 React 函数组件 UserCard: ## 技术栈 - React 19 + TypeScript - Tailwind CSS 4 - shadcn/ui(Card, Avatar, Badge) ## Props 接口 interface UserCardProps { user: { id: string; name: string; email: string; avatar?: string; role: "admin" | "editor" | "viewer"; isOnline: boolean; }; onEdit?: (userId: string) => void; onDelete?: (userId: string) => void; className?: string; } ## 功能描述 显示用户信息卡片,包含头像、姓名、邮箱、角色标签和在线状态指示器。 支持编辑和删除操作按钮。 ## 视觉规范 - 卡片圆角 12px,带轻微阴影 - 头像 48px 圆形,无头像时显示姓名首字母 - 角色标签颜色:admin=红色, editor=蓝色, viewer=灰色 - 在线状态:绿色圆点(在线)/ 灰色圆点(离线) - 悬停时卡片阴影加深 ## 无障碍 - 卡片使用 article 标签 - 操作按钮有 aria-label - 在线状态有 sr-only 文本描述

AI 生成的参考代码结构:

import { forwardRef } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Pencil, Trash2 } from "lucide-react"; import { cn } from "@/lib/utils"; interface UserCardProps { user: { id: string; name: string; email: string; avatar?: string; role: "admin" | "editor" | "viewer"; isOnline: boolean; }; onEdit?: (userId: string) => void; onDelete?: (userId: string) => void; className?: string; } const roleConfig = { admin: { label: "管理员", variant: "destructive" as const }, editor: { label: "编辑者", variant: "default" as const }, viewer: { label: "查看者", variant: "secondary" as const }, }; export const UserCard = forwardRef<HTMLElement, UserCardProps>( ({ user, onEdit, onDelete, className }, ref) => { const initials = user.name .split(" ") .map((n) => n[0]) .join("") .toUpperCase(); return ( <Card ref={ref} className={cn( "transition-shadow hover:shadow-md", className )} > <CardContent className="flex items-center gap-4 p-4"> <div className="relative"> <Avatar className="h-12 w-12"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback>{initials}</AvatarFallback> </Avatar> <span className={cn( "absolute bottom-0 right-0 h-3 w-3 rounded-full border-2 border-white", user.isOnline ? "bg-green-500" : "bg-gray-300" )} > <span className="sr-only"> {user.isOnline ? "在线" : "离线"} </span> </span> </div> <div className="flex-1 min-w-0"> <p className="font-medium truncate">{user.name}</p> <p className="text-sm text-muted-foreground truncate"> {user.email} </p> </div> <Badge variant={roleConfig[user.role].variant}> {roleConfig[user.role].label} </Badge> <div className="flex gap-1"> {onEdit && ( <Button variant="ghost" size="icon" onClick={() => onEdit(user.id)} aria-label={`编辑 ${user.name}`} > <Pencil className="h-4 w-4" /> </Button> )} {onDelete && ( <Button variant="ghost" size="icon" onClick={() => onDelete(user.id)} aria-label={`删除 ${user.name}`} > <Trash2 className="h-4 w-4" /> </Button> )} </div> </CardContent> </Card> ); } ); UserCard.displayName = "UserCard";

2.3 Hooks 生成模式

React Hooks 是 AI 生成中最容易出错的部分。以下是针对不同 Hook 类型的 Prompt 策略。

自定义 Hook 生成 Prompt

生成一个自定义 React Hook:use[HookName] ## 功能 [描述 Hook 的用途和返回值] ## 接口 function use[HookName]([params]): { [returnValue1]: [type]; [returnValue2]: [type]; [action1]: ([params]) => [returnType]; } ## 内部状态 - [state1]: [type] — [描述] - [state2]: [type] — [描述] ## 副作用 - [描述需要的 useEffect 及其依赖] ## 清理逻辑 - [描述组件卸载时需要清理的资源] ## 错误处理 - [描述错误状态和恢复策略] ## 约束 1. 遵循 Hooks 规则(不在条件/循环中调用) 2. 正确设置 useEffect 依赖数组 3. 使用 useCallback 包裹返回的函数 4. 使用 useRef 存储不需要触发重渲染的值 5. 包含 TypeScript 泛型支持(如适用)

实际示例:生成 useDebounce Hook

生成一个自定义 React Hook:useDebounce ## 功能 对输入值进行防抖处理,返回防抖后的值。 支持自定义延迟时间和立即执行选项。 ## 接口 function useDebounce<T>(value: T, delay?: number): T; ## 要求 1. 默认延迟 300ms 2. 组件卸载时清除定时器 3. delay 变化时重置定时器 4. 支持泛型类型 5. 包含 JSDoc 注释

AI 生成的参考代码:

import { useState, useEffect } from "react"; /** * 对输入值进行防抖处理 * @param value - 需要防抖的值 * @param delay - 防抖延迟时间(毫秒),默认 300ms * @returns 防抖后的值 * * @example * const [search, setSearch] = useState(""); * const debouncedSearch = useDebounce(search, 500); * * useEffect(() => { * // 仅在用户停止输入 500ms 后执行搜索 * fetchResults(debouncedSearch); * }, [debouncedSearch]); */ export function useDebounce<T>(value: T, delay = 300): T { const [debouncedValue, setDebouncedValue] = useState<T>(value); useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(timer); }; }, [value, delay]); return debouncedValue; }

常见 Hooks 生成场景与 Prompt 要点

Hook 场景Prompt 关键要点AI 常见错误修正策略
数据获取(useFetch)明确加载/错误/成功状态、取消请求、缓存策略缺少 AbortController 清理、竞态条件要求使用 AbortController,或建议使用 TanStack Query
表单管理(useForm)明确验证规则、提交处理、字段注册方式过度使用 useState、缺少验证建议使用 React Hook Form + Zod
本地存储(useLocalStorage)明确序列化/反序列化、SSR 兼容、类型安全SSR 环境下访问 window 报错要求添加 typeof window 检查
媒体查询(useMediaQuery)明确断点值、SSR 默认值、事件监听清理缺少 SSR 兼容、内存泄漏要求使用 matchMedia API + cleanup
无限滚动(useInfiniteScroll)明确触发阈值、加载状态、IntersectionObserver使用 scroll 事件而非 IntersectionObserver明确要求使用 IntersectionObserver
动画(useAnimation)明确动画类型、时长、缓动函数使用 setTimeout 而非 requestAnimationFrame要求使用 Web Animations API 或 Framer Motion

2.4 React TypeScript Props 模式

Props 类型定义的 Prompt 策略

为以下 React 组件定义 TypeScript Props 接口: ## 组件描述 [描述组件功能] ## Props 要求 1. 必需 props:[列出] 2. 可选 props(带默认值):[列出] 3. 事件回调 props:[列出] 4. 子组件 props:[children 类型] 5. 样式透传:className, style 6. HTML 属性透传:使用 ComponentPropsWithoutRef<"[element]"> ## 类型约束 - 使用 discriminated union 处理互斥 props - 使用泛型处理动态类型 - 使用 Omit/Pick 复用已有类型

高级 Props 模式示例

// 1. 多态组件(as prop) interface ButtonProps<T extends React.ElementType = "button"> { as?: T; variant: "primary" | "secondary" | "ghost"; size: "sm" | "md" | "lg"; isLoading?: boolean; children: React.ReactNode; } type PolymorphicButtonProps<T extends React.ElementType = "button"> = ButtonProps<T> & Omit<React.ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>; // 2. 判别联合(Discriminated Union) type NotificationProps = | { type: "success"; message: string; onDismiss?: () => void } | { type: "error"; message: string; error: Error; onRetry: () => void } | { type: "loading"; message?: string }; // 3. 泛型组件 interface SelectProps<T> { options: T[]; value: T | null; onChange: (value: T) => void; getLabel: (item: T) => string; getValue: (item: T) => string; placeholder?: string; }

2.5 Next.js Server Component vs Client Component 生成

在 Next.js App Router 中,AI 需要正确区分 Server Component 和 Client Component。

Server Component 生成 Prompt

生成一个 Next.js Server Component:[ComponentName] ## 约束(Server Component 规则) - 不使用 useState, useEffect, useRef 等 React hooks - 不使用浏览器 API(window, document, localStorage) - 不使用事件处理器(onClick, onChange 等) - 可以直接 async/await 获取数据 - 可以直接访问数据库、文件系统、环境变量 - 不添加 'use client' 指令 ## 数据获取 [描述需要获取的数据和来源] ## 渲染 [描述 UI 结构]

Client Component 生成 Prompt

生成一个 Next.js Client Component:[ComponentName] ## 约束(Client Component 规则) - 文件顶部添加 'use client' 指令 - 可以使用 React hooks 和浏览器 API - 可以使用事件处理器 - 不能直接访问数据库或文件系统 - 数据通过 props 从 Server Component 传入,或通过 API 获取 ## 交互逻辑 [描述需要的客户端交互] ## 状态管理 [描述组件内部状态]

SC/CC 边界决策表

需求组件类型原因
显示数据库查询结果Server Component直接访问数据源,减少客户端 JS
搜索输入框 + 实时过滤Client Component需要 useState 和 onChange
静态导航栏Server Component无交互需求
带下拉菜单的导航栏混合:外层 SC + 下拉菜单 CC最小化 ‘use client’ 范围
数据表格 + 排序/筛选Client Component需要交互状态
用户头像 + 在线状态Client Component需要实时更新
Markdown 渲染Server Component纯渲染,无交互
表单提交Client Component需要事件处理和验证

3. Vue 组件生成策略

3.1 Vue 3 组件生成的特点

Vue 3 的单文件组件(SFC)格式对 AI 生成有独特的优势和挑战:

优势:

  • SFC 将模板、脚本、样式组织在一个文件中,结构清晰
  • <script setup> 语法简洁,减少样板代码
  • Composition API 的 composable 模式与 React Hooks 类似,AI 迁移知识容易
  • TypeScript 支持通过 defineProps<T>() 泛型语法实现,类型推断优秀

挑战:

  • AI 有时混淆 Options API 和 Composition API
  • <script setup> 的宏(defineProps、defineEmits、defineExpose)AI 偶尔使用错误
  • Vue 的模板语法(v-if、v-for、v-model)与 JSX 差异大,AI 从 React 知识迁移时可能出错
  • Scoped CSS 的 :deep() 选择器 AI 经常遗忘

3.2 Vue SFC 组件生成模式

基础 Vue 组件 Prompt

生成一个 Vue 3 单文件组件 [ComponentName].vue: ## 技术栈 - Vue 3.5+ / <script setup> + TypeScript - 样式方案:[Tailwind CSS / <style scoped>] - 组件库:[无 / Vuetify / PrimeVue / Naive UI] ## Props 使用 defineProps<T>() 泛型语法定义: interface [ComponentName]Props { [propName]: [type]; // [描述] } ## Emits 使用 defineEmits<T>() 泛型语法定义: interface [ComponentName]Emits { (e: '[eventName]', [payload]: [type]): void; } ## 模板要求 - 使用 Vue 模板语法(v-if, v-for, v-model) - v-for 必须包含 :key - 条件渲染使用 v-if/v-else-if/v-else ## 要求 1. 使用 Composition API,不使用 Options API 2. 响应式状态使用 ref() 或 reactive() 3. 计算属性使用 computed() 4. 侦听器使用 watch() 或 watchEffect() 5. 组件命名使用 PascalCase

实际示例:生成 Vue SearchFilter 组件

生成一个 Vue 3 单文件组件 SearchFilter.vue: ## 技术栈 - Vue 3.5 + <script setup> + TypeScript - Tailwind CSS 4 ## Props interface SearchFilterProps { placeholder?: string; // 搜索框占位文本,默认"搜索..." filters: FilterOption[]; // 筛选选项列表 modelValue: string; // 搜索关键词(v-model) activeFilters: string[]; // 当前激活的筛选项(v-model) } interface FilterOption { id: string; label: string; icon?: string; } ## Emits - update:modelValue — 搜索关键词变化 - update:activeFilters — 筛选项变化 - search — 用户按回车或点击搜索按钮 ## 功能 搜索输入框 + 筛选标签组合组件。 输入框支持清除按钮,筛选标签支持多选切换。 ## 无障碍 - 搜索输入有 role="searchbox" - 筛选标签使用 role="checkbox" + aria-checked

AI 生成的参考代码:

<script setup lang="ts"> import { computed } from "vue"; interface FilterOption { id: string; label: string; icon?: string; } interface SearchFilterProps { placeholder?: string; filters: FilterOption[]; modelValue: string; activeFilters: string[]; } const props = withDefaults(defineProps<SearchFilterProps>(), { placeholder: "搜索...", }); const emit = defineEmits<{ "update:modelValue": [value: string]; "update:activeFilters": [value: string[]]; search: []; }>(); const hasValue = computed(() => props.modelValue.length > 0); function clearSearch() { emit("update:modelValue", ""); } function toggleFilter(filterId: string) { const current = [...props.activeFilters]; const index = current.indexOf(filterId); if (index === -1) { current.push(filterId); } else { current.splice(index, 1); } emit("update:activeFilters", current); } function isFilterActive(filterId: string): boolean { return props.activeFilters.includes(filterId); } function handleKeydown(event: KeyboardEvent) { if (event.key === "Enter") { emit("search"); } } </script> <template> <div class="flex flex-col gap-3"> <!-- 搜索输入框 --> <div class="relative"> <svg class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" > <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> </svg> <input role="searchbox" type="text" :value="modelValue" :placeholder="placeholder" class="w-full rounded-lg border border-gray-200 bg-white py-2 pl-10 pr-10 text-sm placeholder:text-gray-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" @keydown="handleKeydown" /> <button v-if="hasValue" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600" aria-label="清除搜索" @click="clearSearch" > </button> </div> <!-- 筛选标签 --> <div v-if="filters.length > 0" class="flex flex-wrap gap-2"> <button v-for="filter in filters" :key="filter.id" role="checkbox" :aria-checked="isFilterActive(filter.id)" class="rounded-full border px-3 py-1 text-sm transition-colors" :class=" isFilterActive(filter.id) ? 'border-blue-500 bg-blue-50 text-blue-700' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-300' " @click="toggleFilter(filter.id)" > <span v-if="filter.icon" class="mr-1">{{ filter.icon }}</span> {{ filter.label }} </button> </div> </div> </template>

3.3 Vue Composable 生成模式

Vue 的 Composable 等同于 React 的自定义 Hook,是逻辑复用的核心机制。

Composable 生成 Prompt

生成一个 Vue 3 Composable:use[Name] ## 功能 [描述 Composable 的用途] ## 接口 function use[Name]([params]): { [returnValue]: Ref<[type]>; [action]: ([params]) => [returnType]; } ## 响应式状态 - 使用 ref() 定义响应式值 - 使用 computed() 定义派生值 - 使用 readonly() 暴露只读状态 ## 生命周期 - onMounted: [描述挂载时的操作] - onUnmounted: [描述卸载时的清理] ## 要求 1. 返回值使用 ref/computed,保持响应式 2. 副作用在 onUnmounted 中清理 3. 支持 SSR(避免直接访问 window/document) 4. 包含 TypeScript 类型定义

实际示例:生成 useCountdown Composable

import { ref, computed, onUnmounted } from "vue"; interface UseCountdownOptions { /** 倒计时总秒数 */ seconds: number; /** 是否自动开始 */ autoStart?: boolean; /** 倒计时结束回调 */ onComplete?: () => void; } interface UseCountdownReturn { /** 剩余秒数 */ remaining: Readonly<Ref<number>>; /** 是否正在运行 */ isRunning: Readonly<Ref<boolean>>; /** 格式化的时间字符串 MM:SS */ formatted: ComputedRef<string>; /** 开始倒计时 */ start: () => void; /** 暂停倒计时 */ pause: () => void; /** 重置倒计时 */ reset: () => void; } export function useCountdown(options: UseCountdownOptions): UseCountdownReturn { const { seconds, autoStart = false, onComplete } = options; const remaining = ref(seconds); const isRunning = ref(false); let intervalId: ReturnType<typeof setInterval> | null = null; const formatted = computed(() => { const mins = Math.floor(remaining.value / 60); const secs = remaining.value % 60; return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`; }); function tick() { if (remaining.value <= 0) { pause(); onComplete?.(); return; } remaining.value--; } function start() { if (isRunning.value) return; isRunning.value = true; intervalId = setInterval(tick, 1000); } function pause() { isRunning.value = false; if (intervalId) { clearInterval(intervalId); intervalId = null; } } function reset() { pause(); remaining.value = seconds; } onUnmounted(() => { if (intervalId) clearInterval(intervalId); }); if (autoStart) start(); return { remaining: readonly(remaining), isRunning: readonly(isRunning), formatted, start, pause, reset, }; }

3.4 Vue 与 React 组件生成对比

维度ReactVue 3Prompt 差异
组件定义函数返回 JSXSFC(template + script + style)Vue 需要指定 SFC 格式
Props 类型interface Props {}defineProps<Props>()Vue 使用编译器宏
事件回调函数 propsdefineEmits<Emits>()Vue 有专门的事件系统
双向绑定受控组件模式v-model + defineModel()Vue 更简洁
条件渲染JSX 三元表达式v-if / v-else模板语法差异
列表渲染array.map()v-forVue 需要 :key
样式className + Tailwind<style scoped> 或 TailwindVue 有原生 scoped CSS
Ref 访问useRef()ref() + template ref概念相似但语法不同
生命周期useEffectonMounted / onUnmountedVue 更明确的生命周期钩子

Vue 特有的 Prompt 注意事项

  1. 始终指定 <script setup>:不指定时 AI 可能生成 Options API 代码
  2. 明确 v-model 用法:Vue 3.4+ 支持 defineModel() 宏,比手动 emit 更简洁
  3. 指定 CSS 方案:Vue 的 <style scoped> 和 Tailwind 可以共存,需要明确偏好
  4. 注意 auto-import:Nuxt 3 项目中 ref、computed 等会自动导入,需要在 Steering 中说明

4. Svelte 组件生成策略

4.1 Svelte 5 Runes 时代的组件生成

Svelte 5 引入了 Runes 系统,彻底改变了响应式编程模型。AI 对 Svelte 的支持正在快速改善,但仍需要精确的 Prompt 引导。

Svelte 5 Runes 核心概念:

Rune用途等价于
$state声明响应式状态React useState / Vue ref()
$derived声明派生值React useMemo / Vue computed()
$effect声明副作用React useEffect / Vue watchEffect()
$props声明组件 propsReact props / Vue defineProps()
$bindable声明可双向绑定的 propVue defineModel()
$inspect开发调试用React DevTools / Vue DevTools

4.2 Svelte 5 组件生成模式

基础 Svelte 5 组件 Prompt

生成一个 Svelte 5 组件 [ComponentName].svelte: ## 技术栈 - Svelte 5(使用 Runes 语法) - TypeScript - 样式:[Tailwind CSS / <style> scoped CSS] ## Props 使用 $props() rune 定义: let { [propName], [propName] = [defaultValue] }: { [propName]: [type]; [propName]?: [type]; } = $props(); ## 响应式状态 使用 $state rune: let [stateName] = $state<[type]>([initialValue]); ## 派生值 使用 $derived rune: let [derivedName] = $derived([expression]); ## 副作用 使用 $effect rune: $effect(() => { [副作用逻辑] }); ## 重要约束 1. 不使用旧版 $: 响应式声明(Svelte 4 语法) 2. 不使用 export let(Svelte 4 props 语法) 3. 使用 Svelte 5 的 Runes 语法 4. 事件处理使用 on[event] 属性而非 on:event 指令

实际示例:生成 Svelte 5 TodoList 组件

生成一个 Svelte 5 组件 TodoList.svelte: ## 技术栈 - Svelte 5 + Runes + TypeScript - Tailwind CSS ## Props interface TodoListProps { title?: string; // 列表标题,默认 "待办事项" } ## 功能 一个完整的待办事项列表组件: - 添加新待办项(输入框 + 回车/按钮) - 切换完成状态(复选框) - 删除待办项 - 显示完成/总数统计 - 筛选:全部/未完成/已完成 ## 无障碍 - 列表使用 ul/li 语义标签 - 复选框有关联的 label - 删除按钮有 aria-label

AI 生成的参考代码:

<script lang="ts"> interface Todo { id: string; text: string; completed: boolean; } type Filter = "all" | "active" | "completed"; let { title = "待办事项" }: { title?: string } = $props(); let todos = $state<Todo[]>([]); let newTodoText = $state(""); let filter = $state<Filter>("all"); let filteredTodos = $derived( todos.filter((todo) => { if (filter === "active") return !todo.completed; if (filter === "completed") return todo.completed; return true; }) ); let stats = $derived({ total: todos.length, completed: todos.filter((t) => t.completed).length, active: todos.filter((t) => !t.completed).length, }); function addTodo() { const text = newTodoText.trim(); if (!text) return; todos.push({ id: crypto.randomUUID(), text, completed: false, }); newTodoText = ""; } function toggleTodo(id: string) { const todo = todos.find((t) => t.id === id); if (todo) todo.completed = !todo.completed; } function deleteTodo(id: string) { const index = todos.findIndex((t) => t.id === id); if (index !== -1) todos.splice(index, 1); } function handleKeydown(event: KeyboardEvent) { if (event.key === "Enter") addTodo(); } </script> <div class="mx-auto max-w-md rounded-lg border bg-white p-6 shadow-sm"> <h2 class="mb-4 text-xl font-semibold">{title}</h2> <!-- 输入区域 --> <div class="mb-4 flex gap-2"> <input type="text" bind:value={newTodoText} placeholder="添加新待办..." class="flex-1 rounded-md border px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" onkeydown={handleKeydown} /> <button onclick={addTodo} disabled={!newTodoText.trim()} class="rounded-md bg-blue-500 px-4 py-2 text-sm font-medium text-white hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed" > 添加 </button> </div> <!-- 筛选标签 --> <div class="mb-3 flex gap-1"> {#each ["all", "active", "completed"] as f} <button onclick={() => (filter = f as Filter)} class="rounded-full px-3 py-1 text-xs transition-colors" class:bg-blue-100={filter === f} class:text-blue-700={filter === f} class:text-gray-500={filter !== f} class:hover:bg-gray-100={filter !== f} > {f === "all" ? "全部" : f === "active" ? "未完成" : "已完成"} </button> {/each} </div> <!-- 待办列表 --> <ul class="space-y-2"> {#each filteredTodos as todo (todo.id)} <li class="flex items-center gap-3 rounded-md border p-3"> <input type="checkbox" id="todo-{todo.id}" checked={todo.completed} onchange={() => toggleTodo(todo.id)} class="h-4 w-4 rounded border-gray-300" /> <label for="todo-{todo.id}" class="flex-1 text-sm" class:line-through={todo.completed} class:text-gray-400={todo.completed} > {todo.text} </label> <button onclick={() => deleteTodo(todo.id)} aria-label="删除 {todo.text}" class="text-gray-400 hover:text-red-500" > </button> </li> {/each} </ul> <!-- 统计 --> {#if todos.length > 0} <p class="mt-3 text-xs text-gray-400"> {stats.completed}/{stats.total} 已完成 </p> {/if} </div>

4.3 Svelte 5 与 React/Vue 的关键差异

维度ReactVue 3Svelte 5AI Prompt 要点
响应式声明useState()ref()$stateSvelte 使用编译器魔法,无需导入
派生值useMemo()computed()$derivedSvelte 自动追踪依赖
副作用useEffect()watchEffect()$effectSvelte 自动清理,无需返回清理函数
Props函数参数解构defineProps<T>()$props()Svelte 使用解构赋值语法
事件回调 propsdefineEmits回调 propsSvelte 5 回归回调模式
双向绑定受控组件v-modelbind:valueSvelte 使用 bind 指令
条件渲染JSX 三元v-if{#if}Svelte 使用块语法
列表渲染map()v-for{#each}Svelte 支持 keyed each
样式className<style scoped><style> (默认 scoped)Svelte 样式默认组件作用域

4.4 Svelte 组件生成的常见 AI 错误

错误描述修正 Prompt
使用 $: 语法AI 生成 Svelte 4 的响应式声明”使用 Svelte 5 Runes 语法($state, $derived, $effect),不使用 $: 声明”
使用 export letAI 生成 Svelte 4 的 props 语法”使用 $props() rune 定义 props,不使用 export let”
使用 on:clickAI 生成 Svelte 4 的事件语法”使用 onclick 属性而非 on:click 指令(Svelte 5 语法)“
使用 createEventDispatcherAI 生成 Svelte 4 的事件派发”使用回调 props 模式传递事件,不使用 createEventDispatcher”
缺少 TypeScriptAI 生成纯 JS 代码”使用 <script lang='ts'> 并为所有变量添加类型注解”
响应式数组操作错误使用 push() 但不触发更新”Svelte 5 的 $state 数组支持直接 push/splice 触发更新”

5. 状态管理代码生成

5.1 状态管理方案选择

不同框架有不同的主流状态管理方案,AI 生成时需要明确指定:

框架推荐方案适用场景价格
ReactZustand轻量全局状态,简单 API开源免费
ReactTanStack Query服务端状态(API 数据缓存)开源免费
ReactJotai原子化状态,细粒度更新开源免费
ReactRedux Toolkit企业级复杂状态,时间旅行调试开源免费
VuePiniaVue 官方状态管理,DevTools 集成开源免费
VueVueUse组合式工具函数集合开源免费
SvelteSvelte Stores内置响应式 store开源免费(内置)
Svelte$state (Runes)Svelte 5 内置响应式开源免费(内置)

5.2 Zustand Store 生成(React)

Zustand Store 生成 Prompt

生成一个 Zustand store:use[StoreName]Store ## 技术栈 - Zustand v5 + TypeScript - 使用 immer 中间件(如需深层更新) - 使用 persist 中间件(如需持久化) ## 状态定义 interface [StoreName]State { // 数据状态 [field]: [type]; // 加载/错误状态 isLoading: boolean; error: string | null; // Actions [actionName]: ([params]) => [returnType]; } ## Actions 描述 1. [actionName]: [描述功能和副作用] 2. [actionName]: [描述功能和副作用] ## 中间件需求 - persist: [是否需要持久化到 localStorage] - devtools: [是否需要 Redux DevTools 支持] - immer: [是否需要不可变更新辅助] ## 选择器 提供细粒度的选择器函数,避免不必要的重渲染

实际示例:生成购物车 Store

import { create } from "zustand"; import { persist, devtools } from "zustand/middleware"; import { immer } from "zustand/middleware/immer"; interface CartItem { id: string; name: string; price: number; quantity: number; image?: string; } interface CartState { items: CartItem[]; isOpen: boolean; // 派生值(通过选择器实现) // totalItems, totalPrice // Actions addItem: (item: Omit<CartItem, "quantity">) => void; removeItem: (id: string) => void; updateQuantity: (id: string, quantity: number) => void; clearCart: () => void; toggleCart: () => void; } export const useCartStore = create<CartState>()( devtools( persist( immer((set) => ({ items: [], isOpen: false, addItem: (item) => set((state) => { const existing = state.items.find((i) => i.id === item.id); if (existing) { existing.quantity += 1; } else { state.items.push({ ...item, quantity: 1 }); } }), removeItem: (id) => set((state) => { state.items = state.items.filter((i) => i.id !== id); }), updateQuantity: (id, quantity) => set((state) => { const item = state.items.find((i) => i.id === id); if (item) { if (quantity <= 0) { state.items = state.items.filter((i) => i.id !== id); } else { item.quantity = quantity; } } }), clearCart: () => set((state) => { state.items = []; }), toggleCart: () => set((state) => { state.isOpen = !state.isOpen; }), })), { name: "cart-storage" } ), { name: "CartStore" } ) ); // 细粒度选择器——避免不必要的重渲染 export const useCartItems = () => useCartStore((s) => s.items); export const useCartOpen = () => useCartStore((s) => s.isOpen); export const useCartTotalItems = () => useCartStore((s) => s.items.reduce((sum, item) => sum + item.quantity, 0)); export const useCartTotalPrice = () => useCartStore((s) => s.items.reduce((sum, item) => sum + item.price * item.quantity, 0) );

5.3 Pinia Store 生成(Vue)

Pinia Store 生成 Prompt

生成一个 Pinia store:use[StoreName]Store ## 技术栈 - Pinia v2 + Vue 3 + TypeScript - 使用 Setup Store 语法(Composition API 风格) ## 状态 - [field]: [type] — [描述] ## Getters(计算属性) - [getterName]: [描述派生逻辑] ## Actions - [actionName]: [描述功能,包括异步操作] ## 要求 1. 使用 Setup Store 语法(defineStore + 函数) 2. 状态使用 ref(),getters 使用 computed() 3. 异步 action 包含 loading/error 状态管理 4. 导出细粒度的 storeToRefs 用法示例

实际示例:生成用户认证 Store

import { ref, computed } from "vue"; import { defineStore } from "pinia"; interface User { id: string; name: string; email: string; role: "admin" | "user"; } export const useAuthStore = defineStore("auth", () => { // 状态 const user = ref<User | null>(null); const token = ref<string | null>(null); const isLoading = ref(false); const error = ref<string | null>(null); // Getters const isAuthenticated = computed(() => !!token.value); const isAdmin = computed(() => user.value?.role === "admin"); const displayName = computed(() => user.value?.name ?? "访客"); // Actions async function login(email: string, password: string) { isLoading.value = true; error.value = null; try { const response = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (!response.ok) { throw new Error("登录失败,请检查邮箱和密码"); } const data = await response.json(); user.value = data.user; token.value = data.token; localStorage.setItem("auth-token", data.token); } catch (err) { error.value = err instanceof Error ? err.message : "未知错误"; throw err; } finally { isLoading.value = false; } } function logout() { user.value = null; token.value = null; localStorage.removeItem("auth-token"); } async function checkAuth() { const savedToken = localStorage.getItem("auth-token"); if (!savedToken) return; token.value = savedToken; isLoading.value = true; try { const response = await fetch("/api/auth/me", { headers: { Authorization: `Bearer ${savedToken}` }, }); if (response.ok) { user.value = await response.json(); } else { logout(); } } catch { logout(); } finally { isLoading.value = false; } } return { // 状态 user: readonly(user), token: readonly(token), isLoading: readonly(isLoading), error: readonly(error), // Getters isAuthenticated, isAdmin, displayName, // Actions login, logout, checkAuth, }; });

5.4 Svelte Store 生成

Svelte 5 模块级状态 Prompt

生成一个 Svelte 5 共享状态模块:[storeName].svelte.ts ## 技术栈 - Svelte 5 Runes + TypeScript - 使用 .svelte.ts 文件扩展名(启用 Runes) ## 状态 使用 $state 在模块级别声明共享状态 ## 派生值 使用 $derived 声明计算值 ## 要求 1. 使用 .svelte.ts 扩展名 2. 导出响应式状态和操作函数 3. 状态变更通过函数封装

实际示例:Svelte 5 主题 Store

// theme.svelte.ts type Theme = "light" | "dark" | "system"; function createThemeStore() { let theme = $state<Theme>("system"); let resolvedTheme = $derived<"light" | "dark">( theme === "system" ? (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : theme ); $effect(() => { if (typeof document === "undefined") return; document.documentElement.classList.toggle("dark", resolvedTheme === "dark"); }); return { get theme() { return theme; }, get resolvedTheme() { return resolvedTheme; }, get isDark() { return resolvedTheme === "dark"; }, setTheme(newTheme: Theme) { theme = newTheme; if (typeof localStorage !== "undefined") { localStorage.setItem("theme", newTheme); } }, toggle() { this.setTheme(resolvedTheme === "dark" ? "light" : "dark"); }, init() { if (typeof localStorage !== "undefined") { const saved = localStorage.getItem("theme") as Theme | null; if (saved) theme = saved; } }, }; } export const themeStore = createThemeStore();

5.5 状态管理生成的通用 Prompt 模式

无论使用哪个框架的状态管理方案,以下 Prompt 模式都适用:

生成 [框架] 状态管理代码: ## 1. 状态模型 定义清晰的 TypeScript 接口,区分: - 数据状态(业务数据) - UI 状态(加载、错误、打开/关闭) - 派生状态(从数据状态计算得出) ## 2. 操作(Actions) 每个操作描述: - 触发条件 - 状态变更逻辑 - 异步操作(如有) - 错误处理 - 乐观更新(如适用) ## 3. 选择器/Getters 定义细粒度的状态访问方式,避免不必要的重渲染/重计算 ## 4. 持久化需求 - 哪些状态需要持久化到 localStorage - 序列化/反序列化策略 - SSR 兼容性 ## 5. 开发工具 - DevTools 集成 - 日志中间件 - 时间旅行调试

6. 样式生成策略

6.1 样式方案对比与 AI 适配度

样式方案AI 适配度Token 效率适用框架价格
Tailwind CSS⭐⭐⭐⭐⭐最高(内联类名)React/Vue/Svelte开源免费
CSS Modules⭐⭐⭐⭐中等(需要额外文件)React/Vue开源免费(内置)
styled-components⭐⭐⭐较低(模板字符串)React开源免费
Emotion⭐⭐⭐较低React开源免费
Vue Scoped CSS⭐⭐⭐⭐高(SFC 内置)Vue开源免费(内置)
Svelte Scoped CSS⭐⭐⭐⭐⭐最高(默认 scoped)Svelte开源免费(内置)
Panda CSS⭐⭐⭐中等React/Vue/Svelte开源免费
vanilla-extract⭐⭐⭐中等React开源免费

为什么 Tailwind CSS 是 AI 生成的最佳选择:

  1. Token 效率最高:样式直接写在 HTML/JSX 中,AI 无需生成额外的样式文件
  2. 训练数据丰富:Tailwind 是 AI 工具(v0.dev、Bolt.new)的默认样式方案
  3. 确定性强:原子类名有明确的映射关系,AI 不容易出错
  4. 无命名负担:不需要为 CSS 类起名,减少 AI 的决策负担
  5. 响应式内置sm: md: lg: 前缀让响应式样式生成更直观

6.2 Tailwind CSS 样式生成

Tailwind 样式生成 Prompt

使用 Tailwind CSS 4 为以下组件生成样式: ## 设计 Token - 主色:[色值] → 映射到 Tailwind 的 [类名] - 圆角:[数值] → rounded-[size] - 阴影:[描述] → shadow-[size] - 间距基准:[数值] ## 响应式断点 - 移动端(默认):[描述布局] - sm(640px+):[描述变化] - md(768px+):[描述变化] - lg(1024px+):[描述变化] ## 暗色模式 - 使用 dark: 前缀 - [描述暗色模式下的颜色变化] ## 动画/过渡 - [描述需要的动画效果] - 使用 transition-[property] duration-[ms] ## 约束 1. 使用 Tailwind 内置类,不写自定义 CSS 2. 颜色使用 CSS 变量(--primary 等)或 Tailwind 色板 3. 使用 cn() 工具函数合并条件类名 4. 避免过长的类名字符串——超过 5 个类时考虑提取

Tailwind 条件样式模式

// 使用 cn() 工具函数(来自 shadcn/ui) import { cn } from "@/lib/utils"; // 变体样式映射 const variants = { primary: "bg-blue-500 text-white hover:bg-blue-600", secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200", danger: "bg-red-500 text-white hover:bg-red-600", } as const; const sizes = { sm: "px-3 py-1.5 text-sm", md: "px-4 py-2 text-base", lg: "px-6 py-3 text-lg", } as const; // 在组件中使用 <button className={cn( "rounded-lg font-medium transition-colors focus:outline-none focus:ring-2", variants[variant], sizes[size], disabled && "opacity-50 cursor-not-allowed", className )} > {children} </button>

6.3 CSS Modules 样式生成

CSS Modules 生成 Prompt

使用 CSS Modules 为以下 React 组件生成样式: ## 文件结构 - [Component].tsx — 组件逻辑 - [Component].module.css — 样式文件 ## 样式要求 1. 使用 CSS 变量引用设计 Token 2. 使用 BEM 命名约定(block__element--modifier) 3. 响应式使用 @media 查询 4. 动画使用 @keyframes 5. 暗色模式使用 [data-theme="dark"] 选择器或 @media (prefers-color-scheme: dark) ## 约束 - 不使用全局样式(:global)除非必要 - 使用 composes 复用样式 - 避免深层嵌套(最多 3 层)

CSS Modules 示例

/* Card.module.css */ .card { border-radius: var(--radius-lg, 12px); border: 1px solid var(--color-border, #e5e7eb); background: var(--color-surface, #ffffff); padding: var(--spacing-4, 16px); transition: box-shadow 0.2s ease; } .card:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); } .card__header { display: flex; align-items: center; gap: var(--spacing-3, 12px); margin-bottom: var(--spacing-3, 12px); } .card__title { font-size: var(--font-size-lg, 1.125rem); font-weight: 600; color: var(--color-text-primary, #111827); } .card__body { color: var(--color-text-secondary, #6b7280); font-size: var(--font-size-sm, 0.875rem); line-height: 1.5; } .card--elevated { composes: card; box-shadow: 0 2px 8px rgb(0 0 0 / 0.08); } /* 响应式 */ @media (max-width: 768px) { .card { padding: var(--spacing-3, 12px); } } /* 暗色模式 */ @media (prefers-color-scheme: dark) { .card { background: var(--color-surface-dark, #1f2937); border-color: var(--color-border-dark, #374151); } }
// Card.tsx import styles from "./Card.module.css"; import { cn } from "@/lib/utils"; interface CardProps { title: string; children: React.ReactNode; elevated?: boolean; className?: string; } export function Card({ title, children, elevated, className }: CardProps) { return ( <div className={cn( elevated ? styles["card--elevated"] : styles.card, className )} > <div className={styles.card__header}> <h3 className={styles.card__title}>{title}</h3> </div> <div className={styles.card__body}>{children}</div> </div> ); }

6.4 styled-components 样式生成

styled-components 生成 Prompt

使用 styled-components v6 为以下 React 组件生成样式: ## 要求 1. 使用 TypeScript 泛型定义 styled props 2. 使用 css helper 提取可复用样式片段 3. 使用 ThemeProvider 的主题变量 4. 支持 props 驱动的动态样式 5. 使用 attrs 设置默认 HTML 属性 ## 主题接口 interface Theme { colors: { primary: string; secondary: string; ... }; spacing: { sm: string; md: string; lg: string; }; radii: { sm: string; md: string; lg: string; }; }

styled-components 示例

import styled, { css } from "styled-components"; interface ButtonStyledProps { $variant: "primary" | "secondary" | "ghost"; $size: "sm" | "md" | "lg"; $fullWidth?: boolean; } const sizeStyles = { sm: css` padding: 6px 12px; font-size: 0.875rem; `, md: css` padding: 8px 16px; font-size: 1rem; `, lg: css` padding: 12px 24px; font-size: 1.125rem; `, }; const variantStyles = { primary: css` background: ${({ theme }) => theme.colors.primary}; color: white; &:hover { opacity: 0.9; } `, secondary: css` background: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.text}; &:hover { opacity: 0.8; } `, ghost: css` background: transparent; color: ${({ theme }) => theme.colors.text}; &:hover { background: ${({ theme }) => theme.colors.hover}; } `, }; export const StyledButton = styled.button<ButtonStyledProps>` border: none; border-radius: ${({ theme }) => theme.radii.md}; font-weight: 500; cursor: pointer; transition: all 0.2s ease; width: ${({ $fullWidth }) => ($fullWidth ? "100%" : "auto")}; ${({ $size }) => sizeStyles[$size]} ${({ $variant }) => variantStyles[$variant]} &:disabled { opacity: 0.5; cursor: not-allowed; } &:focus-visible { outline: 2px solid ${({ theme }) => theme.colors.primary}; outline-offset: 2px; } `;

6.5 样式方案选择决策树

你的项目情况是什么? ├─ 新项目,使用 AI 工具生成 → Tailwind CSS │ 原因:AI 适配度最高,Token 效率最好 ├─ 已有项目,使用 CSS Modules → 继续使用 CSS Modules │ 在 Steering 规则中指定 CSS Modules 约定 ├─ 已有项目,使用 styled-components → 继续使用 │ 在 Steering 规则中指定主题接口和样式模式 ├─ Vue 项目 → <style scoped> + Tailwind CSS │ 两者可以共存,Tailwind 用于布局,scoped CSS 用于复杂样式 ├─ Svelte 项目 → <style> (默认 scoped) + Tailwind CSS │ Svelte 样式默认组件作用域,与 Tailwind 配合良好 └─ 需要运行时主题切换 → CSS 变量 + Tailwind 使用 CSS 变量定义主题 Token,Tailwind 引用变量

7. Prompt 模式库

7.1 按组件类型分类的 Prompt 模式

以下是针对不同组件类型的结构化 Prompt 模板,适用于所有框架。

模式 1:表单组件

生成一个 [框架] 表单组件:[FormName] ## 字段定义 | 字段名 | 类型 | 标签 | 验证规则 | 占位文本 | |--------|------|------|---------|---------| | [name] | text | [标签] | required, minLength(2) | [占位符] | | [email] | email | [标签] | required, email | [占位符] | | [role] | select | [标签] | required | — | ## 验证方案 - React: React Hook Form + Zod - Vue: VeeValidate + Zod 或 Valibot - Svelte: Superforms + Zod ## 提交行为 - 提交时显示 loading 状态 - 成功后 [描述成功行为] - 失败后显示错误消息 ## 无障碍 - 每个输入有关联的 label - 错误消息使用 aria-describedby 关联 - 必填字段标记 aria-required - 表单使用 role="form" + aria-label

模式 2:数据表格组件

生成一个 [框架] 数据表格组件:[TableName] ## 列定义 | 列名 | 字段 | 类型 | 可排序 | 可筛选 | 宽度 | |------|------|------|--------|--------|------| | [列名] | [field] | text | ✓ | ✓ | auto | | [列名] | [field] | number | ✓ | — | 100px | | [列名] | [field] | date | ✓ | ✓ | 150px | | 操作 | — | actions | — | — | 120px | ## 功能 - 排序:点击列头切换升序/降序 - 筛选:[描述筛选方式] - 分页:每页 [N] 条,显示页码导航 - 选择:[单选/多选/无] - 行操作:[编辑/删除/查看详情] ## 数据源 - 数据通过 props 传入 / API 获取 - 加载状态显示骨架屏 - 空状态显示 [描述空状态 UI] ## 响应式 - 桌面端:完整表格 - 移动端:[卡片视图/水平滚动/折叠行]

模式 3:模态框/对话框组件

生成一个 [框架] 模态框组件:[ModalName] ## Props - open: boolean — 控制显示/隐藏 - onClose: () => void — 关闭回调 - title: string — 标题 - size: "sm" | "md" | "lg" — 尺寸 ## 功能 - 点击遮罩层关闭 - 按 Escape 键关闭 - 打开时锁定背景滚动 - 打开时焦点陷阱(focus trap) - 关闭时恢复焦点到触发元素 ## 动画 - 遮罩层:淡入淡出 - 内容区:从底部滑入 + 淡入 ## 无障碍 - role="dialog" + aria-modal="true" - aria-labelledby 指向标题 - 焦点陷阱:Tab 键在模态框内循环 - 关闭按钮有 aria-label

模式 4:导航组件

生成一个 [框架] 导航栏组件:[NavName] ## 结构 - Logo(左侧) - 导航链接(中间):[列出链接项] - 操作区(右侧):[搜索/通知/用户菜单] ## 响应式行为 - 桌面端:水平导航栏 - 移动端:汉堡菜单 + 侧边抽屉 ## 交互 - 当前页面链接高亮 - 下拉菜单(如有子菜单) - 滚动时 [固定/隐藏/缩小] ## 无障碍 - 使用 <nav> + aria-label - 移动端菜单按钮有 aria-expanded - 键盘可导航(Tab + Enter + Escape)

模式 5:列表/卡片网格组件

生成一个 [框架] 卡片网格组件:[GridName] ## 数据 - 数据类型:[描述数据接口] - 数据来源:[props/API/store] ## 布局 - 网格列数:[响应式列数,如 1/2/3/4] - 间距:[描述间距] - 卡片内容:[描述卡片结构] ## 功能 - 加载状态:骨架屏卡片 - 空状态:[描述空状态 UI] - 无限滚动 / 分页 / 加载更多按钮 - 动画:卡片入场动画(stagger) ## 响应式 - 移动端:单列 - 平板:双列 - 桌面:[3-4 列]

7.2 按场景分类的高级 Prompt 模式

模式 6:复合组件模式(Compound Components)

生成一个 [框架] 复合组件:[ComponentName] ## 组件结构 使用复合组件模式(Compound Components),通过子组件组合实现灵活的 API: <[ComponentName]> <[ComponentName].Header>标题</[ComponentName].Header> <[ComponentName].Body>内容</[ComponentName].Body> <[ComponentName].Footer>底部</[ComponentName].Footer> </[ComponentName]> ## 实现方式 - React: 使用 React.createContext + 子组件挂载到父组件 - Vue: 使用 provide/inject + 具名插槽 - Svelte: 使用 Context API + 插槽 ## 共享状态 父组件通过 Context 向子组件传递: - [共享状态 1] - [共享状态 2] ## 要求 1. 子组件只能在父组件内使用 2. 子组件顺序可自由调整 3. 支持条件渲染子组件

模式 7:无限滚动列表

生成一个 [框架] 无限滚动列表组件: ## 数据获取 - API 端点:[描述分页 API] - 每页数量:[N] - 使用 [TanStack Query / 自定义 Hook / Composable] ## 虚拟化 - 列表项高度:[固定/动态] - 使用 [react-window / @tanstack/virtual / 自定义] - 可视区域外的 DOM 节点回收 ## 加载指示 - 底部加载指示器(IntersectionObserver 触发) - 首次加载骨架屏 - 加载失败重试按钮 ## 性能要求 - 10000+ 条数据流畅滚动 - 内存占用稳定(不随数据量线性增长)

模式 8:实时搜索组件

生成一个 [框架] 实时搜索组件: ## 功能 - 输入防抖(300ms) - 搜索结果下拉列表 - 键盘导航(上下箭头选择,Enter 确认,Escape 关闭) - 搜索历史(最近 5 条) - 高亮匹配文本 ## 数据源 - API 搜索:[描述 API] - 本地过滤:[描述本地数据] ## 无障碍 - role="combobox" + aria-expanded - 结果列表 role="listbox" - 选项 role="option" + aria-selected - 实时结果数量通过 aria-live 播报

模式 9:多步骤表单/向导

生成一个 [框架] 多步骤表单组件: ## 步骤定义 | 步骤 | 标题 | 字段 | 验证 | |------|------|------|------| | 1 | [标题] | [字段列表] | [验证规则] | | 2 | [标题] | [字段列表] | [验证规则] | | 3 | [标题] | [字段列表] | [验证规则] | ## 导航 - 步骤指示器(显示当前步骤和总步骤) - 上一步/下一步按钮 - 每步验证通过后才能进入下一步 - 支持直接点击步骤指示器跳转(仅已完成的步骤) ## 状态管理 - 跨步骤的表单数据持久化 - 浏览器刷新后恢复进度(可选) - 最终提交时合并所有步骤数据 ## 动画 - 步骤切换滑动动画(左右方向)

模式 10:仪表板布局组件

生成一个 [框架] 仪表板布局组件: ## 布局结构 - 顶部导航栏(固定) - 左侧边栏(可折叠) - 主内容区(自适应) - 右侧面板(可选,可关闭) ## 侧边栏 - 导航菜单项:[列出菜单项] - 支持多级菜单(折叠/展开) - 折叠模式:仅显示图标 - 当前页面高亮 ## 响应式 - 桌面端:侧边栏常驻 - 平板:侧边栏可折叠 - 移动端:侧边栏变为抽屉(overlay) ## 状态 - 侧边栏折叠状态持久化到 localStorage - 当前路由与菜单项同步

7.3 组件质量验证 Prompt

生成组件后,使用以下 Prompt 进行质量验证:

审查以下 [框架] 组件的质量,检查以下维度: ## 1. TypeScript 类型安全 - Props 接口是否完整且准确 - 是否有 any 类型 - 事件处理器类型是否正确 - 泛型使用是否合理 ## 2. 无障碍合规 - 语义化 HTML 标签使用 - ARIA 属性完整性 - 键盘导航支持 - 颜色对比度 - 屏幕阅读器兼容性 ## 3. 性能 - 是否有不必要的重渲染 - 是否需要 memo/useMemo/useCallback - 事件处理器是否在渲染中创建新引用 - 大列表是否需要虚拟化 ## 4. 错误处理 - 边界情况处理(空数据、null、undefined) - 加载状态和错误状态 - 错误边界(Error Boundary) ## 5. 响应式 - 所有断点是否测试 - 触摸设备兼容性 - 文本溢出处理 ## 6. 测试覆盖 - 是否包含单元测试 - 关键交互是否有测试 - 边界情况是否有测试 请逐项检查并给出改进建议。

8. 组件测试生成策略

8.1 组件测试生成 Prompt

为以下 [框架] 组件生成测试: ## 测试框架 - React: Vitest + @testing-library/react - Vue: Vitest + @vue/test-utils + @testing-library/vue - Svelte: Vitest + @testing-library/svelte ## 测试类型 1. **渲染测试**:组件是否正确渲染 2. **Props 测试**:不同 props 组合的渲染结果 3. **交互测试**:用户操作后的状态变化 4. **无障碍测试**:ARIA 属性和键盘导航 5. **快照测试**:UI 结构稳定性(可选) ## 测试用例 | 用例 | 描述 | 预期结果 | |------|------|---------| | [用例名] | [操作描述] | [预期结果] | ## 约束 1. 使用 @testing-library 的用户行为模拟(userEvent) 2. 查询优先级:getByRole > getByLabelText > getByText > getByTestId 3. 不测试实现细节,测试用户可见的行为 4. 异步操作使用 waitFor / findBy

8.2 React 组件测试示例

import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, it, expect, vi } from "vitest"; import { UserCard } from "./UserCard"; const mockUser = { id: "1", name: "张三", email: "zhangsan@example.com", role: "admin" as const, isOnline: true, }; describe("UserCard", () => { it("渲染用户基本信息", () => { render(<UserCard user={mockUser} />); expect(screen.getByText("张三")).toBeInTheDocument(); expect(screen.getByText("zhangsan@example.com")).toBeInTheDocument(); expect(screen.getByText("管理员")).toBeInTheDocument(); }); it("显示在线状态", () => { render(<UserCard user={mockUser} />); expect(screen.getByText("在线")).toBeInTheDocument(); // sr-only 文本 }); it("显示离线状态", () => { render(<UserCard user={{ ...mockUser, isOnline: false }} />); expect(screen.getByText("离线")).toBeInTheDocument(); }); it("点击编辑按钮触发回调", async () => { const onEdit = vi.fn(); render(<UserCard user={mockUser} onEdit={onEdit} />); await userEvent.click(screen.getByRole("button", { name: /编辑 张三/ })); expect(onEdit).toHaveBeenCalledWith("1"); }); it("不传 onEdit 时不显示编辑按钮", () => { render(<UserCard user={mockUser} />); expect(screen.queryByRole("button", { name: /编辑/ })).not.toBeInTheDocument(); }); it("无头像时显示姓名首字母", () => { render(<UserCard user={{ ...mockUser, avatar: undefined }} />); expect(screen.getByText("张")).toBeInTheDocument(); }); });

实战案例:用 AI 生成完整的电商产品列表模块

案例背景

一个电商项目需要构建产品列表模块,包含:

  • 产品卡片组件
  • 筛选侧边栏
  • 排序下拉菜单
  • 分页组件
  • 购物车集成

技术栈:React + Next.js 15 + TypeScript + Tailwind CSS + Zustand

步骤 1:定义数据模型

首先定义产品列表模块的 TypeScript 类型: interface Product { id: string; name: string; description: string; price: number; originalPrice?: number; image: string; category: string; tags: string[]; rating: number; reviewCount: number; inStock: boolean; } interface ProductFilters { category: string | null; priceRange: [number, number] | null; inStockOnly: boolean; tags: string[]; sortBy: "price-asc" | "price-desc" | "rating" | "newest"; } interface ProductListState { products: Product[]; filters: ProductFilters; currentPage: number; totalPages: number; isLoading: boolean; }

步骤 2:生成 Zustand Store

使用 5.2 节的 Zustand Store 生成 Prompt,创建产品列表的状态管理。

步骤 3:逐组件生成

按照从底层到顶层的顺序生成组件:

1. ProductCard — 单个产品卡片(第一层:UI 结构) 2. ProductGrid — 产品网格布局(第一层:布局) 3. FilterSidebar — 筛选侧边栏(第二层:交互逻辑) 4. SortDropdown — 排序下拉菜单(第二层:交互逻辑) 5. Pagination — 分页组件(第二层:交互逻辑) 6. ProductListPage — 页面组装(第四层:组件组合)

步骤 4:为每个组件编写 Prompt

以 ProductCard 为例:

生成一个 React 函数组件 ProductCard: ## 技术栈 - React 19 + TypeScript + Tailwind CSS + shadcn/ui ## Props interface ProductCardProps { product: Product; onAddToCart: (productId: string) => void; className?: string; } ## 功能 - 显示产品图片(16:9 比例,hover 时轻微放大) - 显示产品名称(最多 2 行,溢出省略) - 显示价格(有原价时显示划线价 + 折扣标签) - 显示评分(星星图标 + 评价数) - 显示库存状态(有货/缺货) - "加入购物车"按钮(缺货时禁用) ## 无障碍 - 卡片使用 article 标签 - 图片有描述性 alt - 按钮有明确的 aria-label - 价格变化有 sr-only 说明

步骤 5:集成测试

生成所有组件后,编写集成测试验证组件间的交互:

为产品列表模块编写集成测试: 1. 筛选操作后产品列表正确更新 2. 排序切换后产品顺序正确 3. 分页切换后显示正确的产品 4. 加入购物车后购物车数量更新 5. 加载状态正确显示骨架屏 6. 空结果显示空状态 UI

案例总结

阶段耗时组件数
数据模型定义5 分钟
Zustand Store 生成10 分钟1
组件逐个生成40 分钟6
人工审查与调整20 分钟
测试生成与验证15 分钟
总计约 90 分钟7

传统手工开发同等模块通常需要 1-2 天。关键在于:逐组件生成、每个组件独立验证、最后组装集成。


避坑指南

❌ 常见错误

  1. 一次性生成整个页面而非逐组件

    • 问题:AI 生成的大型组件往往包含紧耦合的逻辑、重复代码和难以维护的结构
    • 正确做法:将页面拆分为 5-10 个独立组件,逐个生成,每个组件独立验证后再组装
  2. 不指定框架版本和语法偏好

    • 问题:AI 可能生成 Vue 2 Options API、Svelte 4 $: 语法、React Class 组件等过时代码
    • 正确做法:在 Prompt 中明确指定”Vue 3 + <script setup>”、“Svelte 5 Runes”、“React 函数组件”
  3. 忽略 Hooks/Composables 的规则

    • 问题:AI 生成的 React Hooks 可能在条件语句中调用、缺少依赖数组项、产生闭包陷阱
    • 正确做法:在 Prompt 中明确要求”遵循 Hooks 规则”,并使用 ESLint react-hooks/exhaustive-deps 规则验证
  4. 状态管理过度设计

    • 问题:AI 倾向于为简单场景引入 Redux 或复杂的 Context 层级
    • 正确做法:在 Prompt 中明确状态范围——“局部状态用 useState,跨组件状态用 Zustand,服务端状态用 TanStack Query”
  5. 样式方案混用

    • 问题:AI 在同一项目中混用 Tailwind、inline style、CSS Modules,导致样式混乱
    • 正确做法:在 Steering 规则中统一样式方案,Prompt 中明确指定”仅使用 Tailwind CSS,不使用 inline style”
  6. 忽略 TypeScript 严格模式

    • 问题:AI 生成的代码包含 any 类型、缺少 null 检查、类型断言过多
    • 正确做法:在 Prompt 中要求”TypeScript strict mode,不使用 any,使用类型守卫而非类型断言”
  7. 不验证无障碍合规

    • 问题:AI 生成的组件缺少 ARIA 属性、键盘导航、焦点管理
    • 正确做法:在每个组件 Prompt 中包含无障碍要求,生成后运行 axe-core 检查
  8. 复制 AI 生成的测试而不理解

    • 问题:AI 生成的测试可能测试实现细节而非用户行为,或使用错误的查询方式
    • 正确做法:审查测试逻辑,确保使用 @testing-library 的最佳实践(按角色查询、模拟用户行为)

✅ 最佳实践

  1. 建立组件生成 Checklist:每个组件生成后检查——TypeScript 类型完整、无障碍属性齐全、响应式布局正确、测试覆盖关键路径
  2. 使用 Steering 规则统一技术栈:在项目开始前定义框架版本、样式方案、状态管理、测试框架,避免 AI 每次生成时做不同选择
  3. 从 Props 接口开始设计:先定义组件的 TypeScript Props 接口,再让 AI 实现——接口即契约,约束 AI 的生成范围
  4. 逐层验证:先验证 UI 结构和样式(第一层),再添加交互逻辑(第二层),最后集成状态管理(第三层)
  5. 保持组件单一职责:每个组件只做一件事,复杂 UI 通过组合多个简单组件实现
  6. 为每个框架维护 Prompt 模板库:将验证有效的 Prompt 模式保存为模板,团队共享复用
  7. 定期更新 Steering 规则:随着框架版本更新(如 Svelte 5 Runes、Vue 3.5 新特性),及时更新 Steering 规则中的语法偏好

相关资源与延伸阅读

官方文档

  1. React 官方文档 — 组件与 Hooks  — React 19 函数组件和 Hooks 的权威参考
  2. Vue 3 官方文档 — Composition API  — Vue 3 Composition API 和 <script setup> 的完整指南
  3. Svelte 5 官方文档 — Runes  — Svelte 5 Runes 响应式系统的官方文档
  4. Tailwind CSS 文档  — Tailwind CSS 4 的完整类名参考和配置指南
  5. shadcn/ui 组件库  — React + Tailwind 的高质量组件原语,AI 工具的默认组件库

状态管理

  1. Zustand 官方文档  — React 轻量状态管理库,AI 生成友好的简洁 API
  2. Pinia 官方文档  — Vue 官方状态管理库,支持 Composition API 风格的 Setup Store

工具与平台

  1. v0.dev  — Vercel 的 AI UI 生成器,React + Tailwind + shadcn/ui 组件生成的最佳工具
  2. Testing Library 文档  — 前端组件测试的最佳实践,支持 React/Vue/Svelte
  3. TanStack Query 文档  — 服务端状态管理库,支持 React/Vue/Svelte/Angular

参考来源


📖 返回 总览与导航 | 上一节:27a-AI辅助前端开发概览 | 下一节:27c-设计到代码工作流

Last updated on