在Day09中,我們成功完成了設計系統(DesignTokens)的重構,並讓VibePulse擁有了全域深淺色主題切換的能力。
然而,在前端開發與原型驗證的階段,我們經常陷入一個尷尬的境地:後端API與資料庫還沒建好,前端畫面只能充斥著Test1、FooBar或asdfghjk這類無意義的測試字串。
這些缺乏實體意義的測試資料,不僅無法真實反映UI在極端字數、邊界狀態下的排版表現,更無法讓你在展示MVP原型時給予使用者真實的產品Vibe。
今天,我們將示範如何利用AI生成具備領域知識(DomainKnowledge)的高品質JSONMockData,並引導AI完成前端多層次卡片與動態列表的渲染。
1.為什麼高品質MockData是VibeCoding的催化劑?
在VibeCoding的流程中,MockData扮演著前後端契約Bridging的角色:
1.定義TypeScriptSchema(前端/後端共用的型別契約)
2.請AI產生具備領域深度、含角落邊界的結構化JSON資料
3.前端動態渲染(MapList,Progress,Badges)
4.未來後端API完成後,只需更換FetchURL,UI完全無痛銜接
利用LLM生成MockData的最大優勢在於:AI懂業務領域知識。當我們要求AI產生軟體工程師的學習技能清單時,它不會只產生generic的字串,而是會精準給出包含Next.jsAppRouter、PrismaORM、DockerContainerization等符合真實情境的資料。
2.實戰步驟1:定義TypeScriptSchema與生成領域MockData
@types/skill.ts(請參考基礎型別)
你是一位資深的全棧工程師與Domain Expert。請幫我為VibePulse建立一份高品質且貼近現實的TypeScript Mock Data檔案`mocks/skillsData.ts`。
需求細節:
1.結構定義(TypeScript Interface):
export interface SkillItem {
id: string;
title: string;
category: 'Frontend' | 'Backend' | 'AI' | 'DevOps';
proficiency: number; // 0 - 100
lastPracticedAt: string; // ISO Date String
tags: string[];
status: 'learning' | 'mastered' | 'planned';
notesCount: number;
}
資料生成要求(6-8筆資料):
資料必須涵蓋當前熱門技術(Next.js,Tailwind,PythonFastAPI,Docker,ClaudeAPI,PostgreSQL)。
包含邊界狀態:
一筆標題較長的極端資料(測試UI文字截斷/Ellipsis)。
一筆掌握度為0%(planned)的資料。
一筆掌握度為100%(mastered)的資料。
標籤(tags)數量有1個也有4個(測試FlexWrap排版)。
### AI 產出的 `mocks/skillsData.ts`:
export interface SkillItem {
id: string;
title: string;
category: 'Frontend' | 'Backend' | 'AI' | 'DevOps';
proficiency: number;
lastPracticedAt: string;
tags: string[];
status: 'learning' | 'mastered' | 'planned';
notesCount: number;
}
export const mockSkills: SkillItem[] = [
{
id: 'sk-01',
title: 'Next.js App Router & Server Actions 深入實踐',
category: 'Frontend',
proficiency: 85,
lastPracticedAt: '2026-08-11T14:30:00Z',
tags: ['React', 'Next.js', 'SSR', 'Web Dev'],
status: 'learning',
notesCount: 12,
},
{
id: 'sk-02',
title: 'Tailwind CSS v4 & Theme System Design',
category: 'Frontend',
proficiency: 100,
lastPracticedAt: '2026-08-10T09:15:00Z',
tags: ['CSS', 'Styling'],
status: 'mastered',
notesCount: 8,
},
{
id: 'sk-03',
title: 'PostgreSQL & Prisma ORM 資料庫架構設計',
category: 'Backend',
proficiency: 60,
lastPracticedAt: '2026-08-08T18:20:00Z',
tags: ['SQL', 'DB', 'ORM'],
status: 'learning',
notesCount: 5,
},
{
id: 'sk-04',
title: 'Docker 容器化部署與 GitHub Actions CI/CD 流水線搭建',
category: 'DevOps',
proficiency: 35,
lastPracticedAt: '2026-08-05T11:00:00Z',
tags: ['Docker', 'DevOps', 'CI/CD'],
status: 'learning',
notesCount: 3,
},
{
id: 'sk-05',
title: 'LLM Agent 系統架構與 Function Calling 實作',
category: 'AI',
proficiency: 0,
lastPracticedAt: '2026-08-01T00:00:00Z',
tags: ['AI', 'LLM', 'Agent'],
status: 'planned',
notesCount: 0,
},
];
3.實戰步驟2:動態列表渲染與Badge/ProgressBar整合
現在我們請AI撰寫SkillCardList.tsx元件,將這份MockData動態渲染出來,並加入進度條顏色分級與Badge排版。
步驟A:發送渲染組件Prompt
對著@components/SkillCardList.tsx發送Prompt:
@mocks/skillsData.ts(參考MockData結構)
請幫我建立一個用於展示技能列表的React組件`SkillCardList.tsx`。
需求說明:
1.列表排版:使用`gridgrid-cols-1md:grid-cols-2gap-4`排版卡片。
2.卡片細節:
-頂部:類別Badge(根據類別給予不同背景色)、技能標題(過長時點點點`truncate`)、狀態Icon。
-中間:進度條(ProgressBar)與百分比。熟練度>=80為綠色,>=50為藍色,<50為橙色。
-底部:標籤列表(Tags,使用`flex-wrap`)、筆記計數(NotesCount)與最後練習時間(使用`relativetime`格式化,如1天前)。
3.語義化Class:全數採用語義化CSS(如`bg-card`,`border-border`,`text-card-foreground`),適應深淺色模式。
AI產出的SkillCardList.tsx:
import React from 'react';
import { SkillItem, mockSkills } from '@/mocks/skillsData';
import { Badge } from '@/components/ui/badge';
import { BookOpen, Calendar, CheckCircle2, CircleDashed, Flame } from 'lucide-react';
// 類別顏色標籤對應
const categoryStyles: Record<SkillItem['category'], string> = {
Frontend: 'bg-blue-500/10 text-blue-400 border-blue-500/20',
Backend: 'bg-purple-500/10 text-purple-400 border-purple-500/20',
AI: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',
DevOps: 'bg-amber-500/10 text-amber-400 border-amber-500/20',
};
// 計算進度條顏色
const getProgressColor = (val: number) => {
if (val >= 80) return 'bg-emerald-500';
if (val >= 50) return 'bg-blue-500';
return 'bg-amber-500';
};
export default function SkillCardList() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{mockSkills.map((skill) => (
<div
key={skill.id}
className="p-5 rounded-xl bg-card border border-border text-card-foreground hover:border-primary/50 transition-all shadow-sm flex flex-col justify-between space-y-4"
>
{/* Header */}
<div>
<div className="flex items-center justify-between gap-2 mb-2">
<span
className={`text-xs px-2.5 py-0.5 rounded-full border font-medium ${
categoryStyles[skill.category]
}`}
>
{skill.category}
</span>
{skill.status === 'mastered' ? (
<span className="flex items-center text-xs text-emerald-400 gap-1 font-medium">
<CheckCircle2 className="w-3.5 h-3.5" /> 已掌握
</span>
) : (
<span className="flex items-center text-xs text-muted-foreground gap-1">
<CircleDashed className="w-3.5 h-3.5 animate-spin-slow" /> 學習中
</span>
)}
</div>
<h3 className="font-semibold text-base line-clamp-1" title={skill.title}>
{skill.title}
</h3>
</div>
{/* Progress Section */}
<div className="space-y-1.5">
<div className="flex justify-between text-xs font-medium">
<span className="text-muted-foreground">熟練度標記</span>
<span className="text-foreground">{skill.proficiency}%</span>
</div>
<div className="w-full h-2 rounded-full bg-muted overflow-hidden">
<div
className={`h-full transition-all duration-500 ${getProgressColor(
skill.proficiency
)}`}
style={{ width: `${skill.proficiency}%` }}
/>
</div>
</div>
{/* Tags */}
<div className="flex flex-wrap gap-1.5">
{skill.tags.map((tag) => (
<span
key={tag}
className="text-[11px] px-2 py-0.5 rounded-md bg-muted/60 text-muted-foreground"
>
#{tag}
</span>
))}
</div>
{/* Footer Metrics */}
<div className="pt-3 border-t border-border flex items-center justify-between text-xs text-muted-foreground">
<div className="flex items-center gap-1">
<BookOpen className="w-3.5 h-3.5" />
<span>{skill.notesCount} 則筆記</span>
</div>
<div className="flex items-center gap-1">
<Calendar className="w-3.5 h-3.5" />
<span>最近練習:{new Date(skill.lastPracticedAt).toLocaleDateString()}</span>
</div>
</div>
</div>
))}
</div>
);
}
4.邊界測試:無資料狀態(EmptyState)與骨架屏(SkeletonLoading)
完整的VibeCoding原型驗收,不能只看資料載入完美的狀態。我們必須指引AI處理另外兩種關鍵情境:
EmptyState(零資料時):當skills陣列長度為0時,展示插圖或友善的引導按鈕尚無技能,點擊新增第一項技能!。
SkeletonLoading(載入中):在資料非同步載入時,使用ShadcnUI的顯示流暢的灰色骨架屏,提升UX體驗。
補充Prompt範例:@components/SkillCardList.tsx請加入isLoading與skills.length===0的判斷。若isLoading為true,顯示4張卡片;若陣列為空,顯示EmptyState元件與新增第一個技能按鈕。
今天我們解鎖了將VibePulse原型真實化的核心技巧:
透過自然語言+DomainKnowledge生成結構化且擬真的JSONMockData。
使用TypeScript介面綁定,確保前端資料地圖(MapList)渲染具備強型別防護。
涵蓋了字數過長截斷、進度條動態分級、EmptyState與Skeleton載入狀態,徹底擺脫草率的原型感!