Chapter 10

实战与工程化

把前九章拼成一套完整后台面板,落地主题系统与复用策略,附 a11y 清单、v0/AI 工作流与部署要点。

10.1 关键概念

Dashboard 骨架
典型后台三段式:Sidebar(导航)+ Header(面包屑/搜索/用户菜单)+ Main(内容区),用第 6 章的 Sidebar/Sheet 搭建。
组件复用策略
components/ui/ 放 shadcn 原语(尽量少改),components/ 放项目复合组件;业务组合封在功能目录,避免到处复制粘贴。
a11y(可访问性)
Accessibility 缩写。Radix 已给行为兜底,你仍需负责:语义 HTML、Label 关联、图标按钮的 sr-only 文本、对比度、焦点可见。
sr-only
视觉隐藏但屏幕阅读器可读的 class,给纯图标按钮补文字标签的标准做法。
v0.dev
Vercel 的 AI 生成 UI 工具,产出的正是 shadcn/ui + Tailwind 代码,可直接粘进项目,与本教程技术栈无缝衔接。
MCP / AI 工作流
shadcn 提供 MCP server,让 Claude、Cursor 等 AI 助手能直接读 registry、装组件,把"复制源码"自动化进 AI 编码流程。
Tree-shaking
因为组件是项目源码、按需 add,未使用的组件根本不进 bundle,天然按需,无整包引入负担。

10.2 Dashboard 布局骨架

// app/(dashboard)/layout.tsx
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import { AppSidebar } from "@/components/app-sidebar";

export default function Layout({ children }) {
  return (
    <SidebarProvider>
      <AppSidebar />
      <div className="flex-1">
        <header className="flex h-14 items-center gap-2 border-b px-4">
          <SidebarTrigger />
          <Separator orientation="vertical" className="h-4" />
          <h1 className="text-sm font-medium">仪表盘</h1>
        </header>
        <main className="p-4 md:p-6">{children}</main>
      </div>
    </SidebarProvider>
  );
}

10.3 统计卡片区

// 首屏 KPI 卡片网格
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
  {stats.map((s) => (
    <Card key={s.label}>
      <CardHeader className="flex flex-row items-center justify-between pb-2">
        <CardTitle className="text-sm font-medium">{s.label}</CardTitle>
        <s.Icon className="h-4 w-4 text-muted-foreground" />
      </CardHeader>
      <CardContent>
        <div className="text-2xl font-bold">{s.value}</div>
        <p className="text-xs text-muted-foreground">{s.delta}</p>
      </CardContent>
    </Card>
  ))}
</div>

// 下方拼:图表(Chart) + 数据表(DataTable) + 最近活动(卡片列表)

10.4 主题系统落地

// 1) globals.css 定义品牌令牌(第 3 章)
// 2) ThemeProvider 包裹(第 2 章)
// 3) Header 里放 ModeToggle 切换明暗
// 4) 组件一律用语义类:bg-background / text-foreground / border-border
//    禁止硬编码 bg-white / text-black,否则暗色模式失效
最常见的翻车点

写死 bg-whitetext-gray-900 会让暗色模式失灵。始终用语义令牌类(bg-cardtext-muted-foreground),这样切 .dark 时颜色才会自动跟随。

10.5 组件复用与目录约定

src/
├─ components/
│  ├─ ui/            # shadcn 原语,尽量只按需微调
│  │  ├─ button.tsx
│  │  └─ ...
│  ├─ app-sidebar.tsx    # 项目级复合组件
│  ├─ mode-toggle.tsx
│  └─ data-table.tsx
├─ features/         # 按业务域组织的组合
│  └─ users/
│     ├─ columns.tsx
│     └─ user-form.tsx
└─ lib/utils.ts      # cn()
改 ui/ 要克制

ui/ 里的原语尽量保持接近官方,方便未来 re-run CLI 升级时对比。业务定制放到外层复合组件里,别把逻辑塞进原语文件。

10.6 a11y 无障碍清单

检查项做法
图标按钮有名字<span className="sr-only">关闭</span>aria-label
表单 Label 关联Label 的 htmlFor 对上 Input 的 id
Dialog 有标题提供 DialogTitle(可 sr-only 隐藏)
焦点可见别移除 focus-visible:ring,保留焦点环
颜色对比度正文 ≥ 4.5:1,用令牌而非低对比灰
键盘可达所有交互能 Tab 到、Enter/Space 触发
破坏操作确认删除等用 AlertDialog 二次确认
动效可关尊重 prefers-reduced-motion

10.7 与 v0 / AI 工作流

# v0.dev:描述需求 → 生成 shadcn+Tailwind 代码 → 粘进项目
# 因为产出就是 shadcn/ui,无需适配层

# shadcn MCP:让 AI 助手直接操作 registry
$ npx shadcn@latest mcp init   # 配置 MCP server
# 之后可在 Claude/Cursor 里说"帮我加一个 data-table 并接入用户列表"
为什么 shadcn 特别适合 AI 时代

组件是纯文本源码、有清晰命名与结构,AI 极易读懂与生成;"复制进项目"的模式也天然契合 AI"写文件"的能力。v0 与 MCP 让这一切自动化。

10.8 部署要点

10.9 常见坑位速查

症状原因 / 修复
@/ 找不到模块tsconfig paths 与打包器 alias 未同时配
样式没生效Tailwind content 未含组件路径 / 未 import 全局 CSS
暗色不切换写死颜色类,未用语义令牌 / 未加 .dark
Dialog 控制台警告缺 DialogTitle / Description
类名冲突不生效未用 cn(),被 Tailwind 优先级坑

10.10 全教程总结