Claude Code Spec Workflow实战:如何在5分钟内搭建你的第一个AI驱动开发项目

如果你和我一样,曾经在AI编程的浪潮中尝试过各种工具,从Cursor到Claude Code,从GitHub Copilot到各种本地部署的模型,你可能会发现一个共同的问题:AI生成的代码虽然快,但缺乏系统性,后期维护成本高得惊人。那种感觉就像是在沙滩上建城堡,每次潮水(需求变更)一来,整个结构都得重新来过。

我最初接触AI编程时,完全沉浸在所谓的“Vibe Coding”模式中——就是那种想到什么就让AI写什么,没有规划,没有文档,只有即兴的代码片段。刚开始确实很爽,一个下午就能搭出看起来不错的功能原型。但两周后,当我需要添加新功能或者修复一个看似简单的bug时,我发现整个代码库已经变成了一团乱麻。没有清晰的架构,没有规范的文档,甚至连我自己都看不懂上周写的代码逻辑。

直到我遇到了规范驱动开发(Spec-Driven Development) 这个概念,以及它的一个具体实现——Claude Code Spec Workflow。这个开源项目彻底改变了我的开发方式。它不是什么魔法,而是一套系统化的工程方法,让AI的创造力能够被有效地引导和约束,产出真正可维护、可扩展的生产级代码。

今天,我就带你从零开始,用不到5分钟的时间,搭建你的第一个基于Claude Code Spec Workflow的AI驱动项目。我们不会只讲理论,而是通过一个具体的例子——构建一个简单的个人任务管理应用——来展示整个流程。你会发现,一旦掌握了这套方法,AI编程就不再是随意的“聊天”,而是一个可重复、可预测、可协作的工程流程。

1. 环境准备与工具配置:搭建你的AI开发工作台

在开始之前,我们需要确保手头有合适的工具。Claude Code Spec Workflow的核心优势在于它的跨平台兼容性,你可以在多种AI编程环境中使用它,但为了这次演示,我们选择最直接的方式:Claude Code。

1.1 安装Node.js与npm

这个工作流基于Node.js构建,所以首先需要确保你的系统已经安装了Node.js环境。如果你还没有安装,可以按照以下步骤操作:

对于macOS用户:

# 使用Homebrew安装
brew install node

# 验证安装
node --version
npm --version

对于Windows用户:

  1. 访问 Node.js官网 下载LTS版本安装包
  2. 运行安装程序,按照向导完成安装
  3. 打开命令提示符或PowerShell验证:
node --version
npm --version

对于Linux用户(Ubuntu/Debian为例):

# 使用NodeSource仓库安装
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

# 验证安装
node --version
npm --version

注意:建议安装Node.js 18.x或更高版本,以确保所有依赖都能正常工作。如果你已经安装了旧版本,可以使用nvm(Node Version Manager)来管理多个版本。

1.2 安装Claude Code Spec Workflow

安装过程非常简单,只需要一条命令。打开你的终端(无论是VS Code的内置终端、iTerm2还是Windows Terminal),执行:

npm install -g @pimzino/claude-code-spec-workflow

这个命令会全局安装工作流工具,让你可以在任何项目目录中使用它。安装完成后,你可以验证安装是否成功:

claude-code-spec-workflow --version

如果看到版本号输出,说明安装成功。接下来,我们需要创建一个新的项目目录:

# 创建一个专门用于实验的目录
mkdir ai-spec-project
cd ai-spec-project

# 初始化工作流
claude-code-spec-workflow init

初始化过程会创建几个关键文件:

  • .claude/ 目录:存放工作流的配置和缓存
  • steering/ 目录:存放项目的指导性文档
  • 基本的项目结构配置文件

1.3 配置你的AI编程环境

Claude Code Spec Workflow支持多种AI编程工具,但配置方式略有不同。下面我列出几种常见工具的配置方法:

在Claude Code中配置:

# 在项目目录中运行
claude mcp add spec-workflow npx @pimzino/spec-workflow-mcp@latest /path/to/your/project

在Cursor中配置: 你需要编辑Cursor的MCP服务器配置。打开Cursor的设置,找到MCP服务器部分,添加以下配置:

{
  "mcp.servers": {
    "spec-workflow": {
      "command": "npx",
      "args": ["-y", "@pimzino/spec-workflow-mcp@latest", "/path/to/your/project"]
    }
  }
}

在Claude Desktop中配置: 配置文件通常位于 ~/.config/Claude/claude_desktop_config.json(macOS/Linux)或 %APPDATA%\Claude\claude_desktop_config.json(Windows),添加:

{
  "mcpServers": {
    "spec-workflow": {
      "command": "npx",
      "args": ["-y", "@pimzino/spec-workflow-mcp@latest", "/path/to/your/project"]
    }
  }
}

提示:将 /path/to/your/project 替换为你实际的项目路径。在macOS/Linux上,你可以使用 pwd 命令获取当前路径;在Windows上,使用 cd 然后复制地址栏的路径。

1.4 启动可视化仪表盘(可选但推荐)

工作流还提供了一个Web仪表盘,可以实时查看项目进度和文档。启动它非常简单:

npx -y @pimzino/spec-workflow-mcp@latest /path/to/your/project --dashboard

# 如果你想使用自定义端口(比如3000)
npx -y @pimzino/spec-workflow-mcp@latest /path/to/your/project --dashboard --port 3000

启动后,在浏览器中打开 http://localhost:3000(或你指定的端口),就能看到一个清晰的项目管理界面。这个仪表盘特别适合团队协作,项目经理或产品经理即使不懂代码,也能通过它了解开发进度。

2. 创建项目指导文档:为AI设定清晰的“北极星”

现在环境已经准备好了,我们可以开始真正的项目开发。与传统开发不同,规范驱动开发的第一步不是写代码,而是创建指导文档。这些文档就像是项目的“宪法”,为后续的所有开发工作提供方向和约束。

2.1 理解指导文档的三层结构

Claude Code Spec Workflow的指导文档体系分为三个核心部分,我通常把它们称为“项目三支柱”:

文档类型核心内容作用示例要点
产品愿景文档 (product.md)产品目标、用户画像、核心价值主张定义“为什么做”和“为谁做”解决什么用户痛点、期望达到什么商业目标
技术栈文档 (tech.md)技术选型、架构决策、开发规范定义“用什么做”和“怎么做”前端框架、后端技术、数据库、部署方式
项目结构文档 (structure.md)目录结构、模块划分、接口设计定义“代码如何组织”组件分层、API端点设计、数据流方向

这三份文档共同构成了项目的上下文边界。当AI生成代码时,它会严格遵循这些文档中的约定,确保产出的一致性。

2.2 为任务管理应用创建产品愿景

让我们以构建一个“个人任务管理应用”为例。首先创建产品愿景文档:

# 在工作流中启动产品文档创建
/spec-steering-setup product

系统会引导你回答一系列问题。以下是我在实际项目中使用的回答示例:

应用名称: TaskFlow Pro 目标用户: 需要高效管理个人任务的专业人士、学生、自由职业者 核心问题: 现有任务管理工具要么过于复杂(如Jira),要么功能过于简单(如原生待办事项),缺乏智能化的任务优先级和进度跟踪 核心价值: 提供基于AI的任务智能分类、自动优先级排序、专注时间跟踪的一体化解决方案 关键特性:

  1. 智能任务录入(支持自然语言解析)
  2. 基于艾森豪威尔矩阵的自动优先级分类
  3. 番茄工作法集成的时间跟踪
  4. 跨设备同步(Web + 移动端PWA)
  5. 每周/每月完成情况的可视化报告

用户故事示例:

  • 作为忙碌的专业人士,我希望能够用自然语言快速添加任务(如“明天下午3点与团队开会讨论Q3规划”),以便节省手动设置所有字段的时间
  • 作为有拖延倾向的用户,我希望系统能自动识别重要且紧急的任务并置顶显示,以便我优先处理最关键的事项
  • 作为自由职业者,我希望能够跟踪在每个任务上花费的实际时间,以便准确向客户计费

2.3 定义技术栈与架构决策

接下来创建技术栈文档。这是最关键的一步,因为它决定了整个项目的技术走向:

/spec-steering-setup tech

对于我们的任务管理应用,我选择了以下技术栈:

前端技术选型:

  • 框架: React 18 + TypeScript(类型安全,开发体验好)
  • UI库: Tailwind CSS + Headless UI(快速原型,高度定制)
  • 状态管理: Zustand(轻量,学习曲线平缓)
  • 路由: React Router v6
  • 图表: Recharts(轻量,功能足够)
  • 表单处理: React Hook Form + Zod(类型安全的表单验证)

后端技术选型(第一版简化):

  • 运行时: Node.js + Express(快速启动)
  • 数据库: SQLite(开发环境)+ PostgreSQL(生产环境)
  • ORM: Prisma(类型安全,迁移方便)
  • 认证: JWT + bcrypt

开发规范:

  • 代码风格: ESLint + Prettier统一配置
  • 提交规范: Conventional Commits
  • 测试: Vitest + React Testing Library(单元测试)+ Playwright(E2E测试)
  • 代码分割: 按路由动态导入
  • 错误处理: 统一的错误边界和日志系统

性能要求:

  • 首屏加载时间 < 2秒(3G网络)
  • 交互响应时间 < 100ms
  • 支持离线使用(PWA特性)
  • 包体积优化(Tree Shaking + 代码分割)

2.4 设计项目结构与模块划分

最后是项目结构文档,这决定了代码的组织方式:

/spec-steering-setup structure

我设计的结构如下:

taskflow-pro/
├── src/
│   ├── components/          # 可复用UI组件
│   │   ├── common/         # 按钮、输入框等基础组件
│   │   ├── tasks/          # 任务相关专用组件
│   │   └── layout/         # 布局组件
│   ├── features/           # 功能模块(按领域划分)
│   │   ├── task-management/
│   │   │   ├── api/       # API调用层
│   │   │   ├── components/# 功能专用组件
│   │   │   ├── hooks/     # 自定义Hook
│   │   │   └── types/     # TypeScript类型定义
│   │   └── time-tracking/
│   ├── lib/               # 工具函数和第三方集成
│   ├── pages/             # 页面组件(路由级别)
│   ├── stores/            # Zustand状态存储
│   └── utils/             # 通用工具函数
├── server/                 # 后端代码
│   ├── api/               # API路由
│   ├── middleware/        # 中间件
│   └── utils/             # 服务端工具函数
└── public/                # 静态资源

这种结构有几个明显优势:

  1. 关注点分离:功能模块内聚,减少跨模块依赖
  2. 可测试性:每个模块可以独立测试
  3. 可维护性:新人上手快,知道在哪里找什么代码
  4. 可扩展性:添加新功能时影响范围小

经验分享:我在实际项目中发现,花30分钟仔细设计这些指导文档,可以节省后续数小时的调试和重构时间。AI有了清晰的“上下文边界”后,生成的代码质量会有质的提升。

3. 从需求到实现:规范驱动开发的全流程演练

有了指导文档作为基础,我们现在可以开始真正的功能开发了。规范驱动开发的核心流程是:需求 → 设计 → 任务分解 → 实现。让我们以“智能任务创建”功能为例,看看这个流程如何运作。

3.1 创建详细的需求规范

首先,我们使用工作流的spec-create命令来创建需求文档:

/spec-create smart-task-creation "实现基于自然语言解析的智能任务创建功能"

AI会引导你完善需求细节。以下是我在实际对话中提供的需求:

功能描述: 用户可以通过自然语言输入任务描述,系统自动解析出任务标题、截止时间、优先级、标签等信息,并预填充到表单中,用户只需确认或微调即可创建任务。

用户场景:

  1. 用户输入:“明天下午3点与团队开会讨论Q3规划,需要准备PPT,高优先级”
  2. 系统解析出:
    • 标题:与团队开会讨论Q3规划
    • 截止时间:明天15:00
    • 优先级:高
    • 标签:会议、规划、PPT
    • 备注:需要准备PPT
  3. 用户确认或修改后,一键创建任务

技术需求:

  • 前端:自然语言输入框,实时解析预览,可编辑的表单
  • 后端:自然语言处理API端点,支持日期时间解析、关键词提取
  • 算法:简单的规则引擎(正则表达式) + 可扩展的NLP模型集成

验收标准:

  1. 输入自然语言后,1秒内显示解析结果
  2. 解析准确率 > 90%(对常见时间格式、优先级关键词)
  3. 用户可以对解析出的任何字段进行编辑
  4. 支持中英文混合输入
  5. 提供解析失败时的优雅降级(转为普通表单)

AI收到这些需求后,会生成一份结构化的需求文档,通常包括:

  • 功能概述
  • 用户故事(User Stories)
  • 验收标准(Acceptance Criteria)
  • 技术约束
  • 非功能性需求(性能、可访问性等)

3.2 生成技术设计方案

需求确认后,AI会自动进入设计阶段。它会基于之前创建的tech.md和structure.md,生成详细的技术设计方案:

# AI会自动进行,你也可以手动触发
/spec-design smart-task-creation

生成的设计文档通常包含以下部分:

架构设计:

// 这是AI可能会建议的接口设计示例
interface ParsedTask {
  title: string;
  dueDate?: Date;
  priority: 'low' | 'medium' | 'high' | 'urgent';
  tags: string[];
  estimatedDuration?: number; // 分钟
  notes?: string;
}

interface NLPResponse {
  success: boolean;
  data?: ParsedTask;
  error?: string;
  confidence: number; // 置信度0-1
}

// API端点设计
POST /api/tasks/parse
Request: { text: string }
Response: NLPResponse

POST /api/tasks
Request: ParsedTask
Response: { id: string; ... }

前端组件设计:

  • SmartTaskInput:智能输入组件,包含文本输入和实时预览
  • TaskPreviewCard:解析结果预览卡片
  • TaskForm:可编辑的任务表单(基于解析结果预填充)

后端服务设计:

  1. 解析服务层:处理自然语言解析逻辑
  2. 规则引擎:基于正则表达式的快速解析
  3. NLP集成:可插拔的NLP服务(初期用简单规则,后期可接入LLM)
  4. 缓存层:缓存常见模式的解析结果

数据流设计:

用户输入 → 前端防抖(300ms) → API调用 → 规则引擎解析 → 返回结果 → 前端预览 → 用户确认/编辑 → 提交创建

3.3 任务分解与原子化

设计确认后,AI会将整个功能分解为原子化的任务。这是规范驱动开发最强大的部分之一——将复杂功能拆解为AI可以独立完成的小任务。

# 查看生成的任务列表
/spec-tasks smart-task-creation

对于我们的智能任务创建功能,AI可能会生成类似这样的任务列表:

阶段1:基础架构搭建

  1. 创建TypeScript类型定义(ParsedTask, NLPResponse等)
  2. 设置前端项目结构(安装依赖,配置构建工具)
  3. 配置后端Express服务器和基础路由

阶段2:规则引擎实现 4. 实现日期时间解析器(支持“明天”、“下周一下午3点”等格式) 5. 实现优先级关键词识别(高、中、低、紧急等) 6. 实现标签提取算法(从文本中提取关键词作为标签) 7. 创建解析服务的单元测试

阶段3:前端组件开发 8. 创建SmartTaskInput组件(带防抖的文本输入) 9. 实现实时解析预览功能(调用API并显示结果) 10. 创建可编辑的TaskForm组件 11. 实现表单验证和错误处理 12. 添加加载状态和错误状态UI

阶段4:后端API开发 13. 实现POST /api/tasks/parse端点 14. 集成规则引擎到API层 15. 添加请求验证和错误处理中间件 16. 实现POST /api/tasks创建端点

阶段5:集成与测试 17. 前后端联调 18. 编写E2E测试(用户完整流程) 19. 性能优化(缓存、防抖、错误重试) 20. 文档编写和代码审查

每个任务都是自包含的,有明确的输入输出,可以独立完成和测试。AI会为每个任务生成具体的实现指令。

3.4 自动化任务执行

任务分解完成后,你可以选择手动执行每个任务,或者让AI自动执行。对于初学者,我建议先手动执行几个任务,了解工作流程:

# 执行第一个任务:创建TypeScript类型定义
/spec-execute 1 smart-task-creation

AI会开始工作,生成相应的代码。以第一个任务为例,它可能会创建以下文件:

src/features/task-management/types/task.ts

export type TaskPriority = 'low' | 'medium' | 'high' | 'urgent';

export interface Task {
  id: string;
  title: string;
  description?: string;
  dueDate?: Date;
  priority: TaskPriority;
  tags: string[];
  completed: boolean;
  createdAt: Date;
  updatedAt: Date;
  estimatedDuration?: number; // 分钟
  actualDuration?: number; // 实际花费分钟
}

export interface ParsedTask {
  title: string;
  description?: string;
  dueDate?: Date;
  priority: TaskPriority;
  tags: string[];
  estimatedDuration?: number;
  notes?: string;
}

export interface NLPRequest {
  text: string;
  language?: 'zh' | 'en';
}

export interface NLPResponse {
  success: boolean;
  data?: ParsedTask;
  error?: string;
  confidence: number;
  warnings?: string[];
}

// 时间解析相关的类型
export interface DateTimeParseResult {
  type: 'date' | 'time' | 'datetime' | 'relative';
  value: Date;
  text: string;
  confidence: number;
}

src/features/task-management/types/api.ts

import { ParsedTask, Task, NLPRequest, NLPResponse } from './task';

export interface CreateTaskRequest extends ParsedTask {
  // 可以添加额外的字段
}

export interface CreateTaskResponse {
  success: boolean;
  data?: Task;
  error?: string;
}

// API端点类型
export type ParseTaskEndpoint = {
  request: NLPRequest;
  response: NLPResponse;
};

export type CreateTaskEndpoint = {
  request: CreateTaskRequest;
  response: CreateTaskResponse;
};

完成一个任务后,你可以继续执行下一个:

# 执行第二个任务:设置前端项目结构
/spec-execute 2 smart-task-creation

AI会检查当前项目状态,然后生成或修改相应的配置文件。这个过程会一直持续,直到所有任务完成。

实用技巧:我发现在执行任务时,保持Claude Code或Cursor的对话窗口打开很有帮助。你可以实时看到AI的思考过程,如果它做出了你不理解或不认同的决策,可以及时干预。这种“人在循环”的模式既利用了AI的效率,又保留了人类的判断力。

4. 错误处理与优化技巧:避开常见陷阱

在实际使用Claude Code Spec Workflow的过程中,你会遇到各种挑战。下面是我在多个项目中总结出的经验教训和优化技巧。

4.1 常见错误模式及解决方案

问题1:AI生成的代码不符合项目规范

这是最常见的问题。AI可能会使用不同的代码风格、引入不必要的依赖,或者忽略项目的特定约定。

解决方案:

  1. 强化指导文档:在tech.md中明确代码规范
  2. 使用更具体的提示:在任务描述中加入约束条件
  3. 及时纠正并反馈:当AI偏离方向时,立即停止并给出明确反馈

例如,如果你希望使用特定的React模式,可以在任务描述中明确:

任务:创建SmartTaskInput组件
要求:
- 使用函数组件而非类组件
- 使用TypeScript,严格类型检查
- 遵循项目的命名约定(camelCase for functions, PascalCase for components)
- 使用Tailwind CSS进行样式,不引入额外的CSS文件
- 实现完整的键盘导航支持
- 添加ARIA属性以支持可访问性
- 编写PropTypes或TypeScript接口

问题2:任务分解过于细碎或过于笼统

不合理的任务分解会导致效率低下。过于细碎会增加管理开销,过于笼统则AI难以一次完成。

解决方案:

  • 黄金法则:每个任务应该在15-30分钟内完成
  • 检查点:复杂的任务应该包含明确的检查点
  • 依赖管理:确保任务有清晰的依赖关系

我通常使用以下标准来判断任务分解是否合理:

任务特征合理需要调整
完成时间15-60分钟<15分钟或>2小时
输出明确可验证的具体产出模糊的“实现功能”
依赖清晰知道需要什么前置条件依赖不明确
测试可行可以编写单元测试难以独立测试

问题3:上下文丢失或混乱

当项目变大、文件变多时,AI可能会忘记之前的决策或引入不一致的更改。

解决方案:

  1. 定期更新指导文档:当架构决策变化时,及时更新tech.md和structure.md
  2. 使用工作流的上下文管理:Claude Code Spec Workflow有智能的上下文缓存机制
  3. 分模块开发:一次只关注一个功能模块,减少上下文负担

4.2 性能优化技巧

Token使用优化: Claude Code Spec Workflow内置了Token优化机制,但你还可以进一步优化:

# 查看当前项目的Token使用情况
claude-code-spec-workflow stats

# 清理不必要的缓存
claude-code-spec-workflow clean-cache

我的Token优化策略:

  1. 分块处理大文件:对于超过1000行的文件,让AI分多次处理
  2. 优先处理核心逻辑:先让AI实现业务逻辑,再处理样式和细节
  3. 使用代码片段而非完整文件:当只需要修改一小部分时,提供具体的代码位置

响应速度优化:

  • 使用本地模型:如果条件允许,搭配本地运行的代码模型(如CodeLlama)
  • 批量处理相关任务:一次性执行多个相关的小任务
  • 避免频繁的上下文切换:一次会话中专注于一个功能模块

4.3 质量保证最佳实践

代码审查清单: 即使AI生成的代码,也需要人工审查。我使用以下清单:

## AI生成代码审查清单

### 架构一致性
- [ ] 是否符合tech.md中的技术决策?
- [ ] 是否遵循structure.md中的项目结构?
- [ ] 是否引入了不必要的依赖?

### 代码质量
- [ ] 类型安全(TypeScript错误为0)
- [ ] 没有明显的性能问题(如重复渲染、内存泄漏)
- [ ] 错误处理是否完备?
- [ ] 是否有适当的日志记录?

### 安全性
- [ ] 输入验证是否到位?
- [ ] 是否有敏感信息硬编码?
- [ ] API端点是否有适当的认证/授权?

### 测试覆盖
- [ ] 是否包含单元测试?
- [ ] 测试是否覆盖主要用例和边界情况?
- [ ] 测试是否可读且可维护?

### 文档
- [ ] 复杂逻辑是否有注释?
- [ ] 公共API是否有文档?
- [ ] README是否更新?

自动化测试集成: 在指导文档中明确测试要求,AI会生成相应的测试代码。例如:

// AI生成的测试示例
import { parseNaturalLanguage } from './parser';
import { describe, it, expect } from 'vitest';

describe('自然语言解析器', () => {
  it('应该解析包含日期的任务', () => {
    const input = '明天下午3点开会';
    const result = parseNaturalLanguage(input);
    
    expect(result.success).toBe(true);
    expect(result.data?.dueDate).toBeInstanceOf(Date);
    expect(result.confidence).toBeGreaterThan(0.8);
  });

  it('应该处理解析失败的情况', () => {
    const input = '这是一段无法解析的文本';
    const result = parseNaturalLanguage(input);
    
    expect(result.success).toBe(false);
    expect(result.error).toBeDefined();
  });

  it('应该支持中英文混合输入', () => {
    const input = '明天meeting with team about Q3 planning';
    const result = parseNaturalLanguage(input, 'zh');
    
    expect(result.success).toBe(true);
    expect(result.data?.tags).toContain('meeting');
  });
});

4.4 团队协作策略

如果你在团队中使用Claude Code Spec Workflow,以下策略会很有帮助:

版本控制集成:

  1. 规范文档即代码:将product.md、tech.md、structure.md纳入版本控制
  2. AI生成的代码也需要审查:建立代码审查流程,不因为是AI生成就降低标准
  3. 分支策略:为每个功能规范创建独立分支

知识共享:

  1. 规范库:建立可复用的规范模板库
  2. 经验文档:记录AI工作的最佳实践和常见问题
  3. 定期回顾:团队定期回顾AI生成代码的质量和效率

分工协作:

  • 架构师/技术负责人:负责创建和维护指导文档
  • 开发人员:负责编写详细规范和执行任务
  • 质量保证:负责审查AI生成的代码和测试

5. 高级应用场景:超越基础任务管理

掌握了基础流程后,让我们看看Claude Code Spec Workflow在一些更复杂场景下的应用。这些是我在实际项目中验证过的模式。

5.1 复杂状态管理:Zustand与React Query集成

在现代前端应用中,状态管理是一个复杂但关键的部分。让我们看看如何用规范驱动的方式实现一个复杂的状态管理方案。

需求规范示例:

功能:任务状态管理
要求:
1. 使用Zustand作为状态管理库
2. 集成React Query用于服务器状态
3. 实现乐观更新(optimistic updates)
4. 支持离线功能(本地优先,同步到服务器)
5. 完整的错误处理和重试逻辑

AI生成的技术设计可能包括:

// store/taskStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import type { Task, CreateTaskRequest } from '../features/task-management/types';

interface TaskStore {
  // 本地状态
  tasks: Task[];
  selectedTaskId: string | null;
  filters: {
    status: 'all' | 'active' | 'completed';
    priority: TaskPriority | 'all';
    tags: string[];
  };
  
  // 操作
  addTask: (task: Task) => void;
  updateTask: (id: string, updates: Partial<Task>) => void;
  deleteTask: (id: string) => void;
  setFilters: (filters: Partial<TaskStore['filters']>) => void;
  
  // 派生状态
  getFilteredTasks: () => Task[];
  getTaskById: (id: string) => Task | undefined;
}

// 创建store
export const useTaskStore = create<TaskStore>()(
  persist(
    (set, get) => ({
      tasks: [],
      selectedTaskId: null,
      filters: {
        status: 'all',
        priority: 'all',
        tags: [],
      },
      
      addTask: (task) => set((state) => ({ 
        tasks: [...state.tasks, task] 
      })),
      
      updateTask: (id, updates) => set((state) => ({
        tasks: state.tasks.map(task => 
          task.id === id ? { ...task, ...updates, updatedAt: new Date() } : task
        )
      })),
      
      deleteTask: (id) => set((state) => ({
        tasks: state.tasks.filter(task => task.id !== id)
      })),
      
      setFilters: (newFilters) => set((state) => ({
        filters: { ...state.filters, ...newFilters }
      })),
      
      getFilteredTasks: () => {
        const { tasks, filters } = get();
        return tasks.filter(task => {
          // 状态过滤
          if (filters.status === 'active' && task.completed) return false;
          if (filters.status === 'completed' && !task.completed) return false;
          
          // 优先级过滤
          if (filters.priority !== 'all' && task.priority !== filters.priority) return false;
          
          // 标签过滤
          if (filters.tags.length > 0) {
            const hasAllTags = filters.tags.every(tag => task.tags.includes(tag));
            if (!hasAllTags) return false;
          }
          
          return true;
        });
      },
      
      getTaskById: (id) => {
        return get().tasks.find(task => task.id === id);
      },
    }),
    {
      name: 'task-storage', // localStorage的key
      partialize: (state) => ({ tasks: state.tasks }), // 只持久化tasks
    }
  )
);

// React Query集成
export const useTasksQuery = () => {
  return useQuery({
    queryKey: ['tasks'],
    queryFn: async () => {
      const response = await fetch('/api/tasks');
      if (!response.ok) throw new Error('Failed to fetch tasks');
      return response.json();
    },
    // 初始数据从本地存储获取
    initialData: () => useTaskStore.getState().tasks,
  });
};

export const useCreateTaskMutation = () => {
  const queryClient = useQueryClient();
  const { addTask } = useTaskStore();
  
  return useMutation({
    mutationFn: async (taskData: CreateTaskRequest) => {
      const response = await fetch('/api/tasks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(taskData),
      });
      if (!response.ok) throw new Error('Failed to create task');
      return response.json();
    },
    
    // 乐观更新
    onMutate: async (newTask) => {
      // 取消任何正在进行的refetch
      await queryClient.cancelQueries({ queryKey: ['tasks'] });
      
      // 保存当前状态以便回滚
      const previousTasks = queryClient.getQueryData(['tasks']);
      
      // 乐观更新
      const optimisticTask: Task = {
        ...newTask,
        id: `temp-${Date.now()}`,
        completed: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      
      queryClient.setQueryData(['tasks'], (old: Task[] = []) => [...old, optimisticTask]);
      addTask(optimisticTask);
      
      return { previousTasks };
    },
    
    onError: (err, newTask, context) => {
      // 出错时回滚
      queryClient.setQueryData(['tasks'], context?.previousTasks);
    },
    
    onSettled: () => {
      // 无论成功失败都重新获取
      queryClient.invalidateQueries({ queryKey: ['tasks'] });
    },
  });
};

5.2 响应式设计与移动端优化

对于任务管理应用,移动端体验至关重要。让我们看看如何用规范驱动的方式实现响应式设计。

设计规范示例:

// 在tech.md中定义的响应式断点
export const breakpoints = {
  sm: '640px',
  md: '768px',
  lg: '1024px',
  xl: '1280px',
  '2xl': '1536px',
} as const;

// 在structure.md中定义的组件响应式策略
export const responsiveStrategies = {
  // 移动端优先
  mobileFirst: true,
  
  // 组件断点行为
  components: {
    TaskList: {
      base: 'grid grid-cols-1 gap-4',
      md: 'grid grid-cols-2 gap-6',
      lg: 'grid grid-cols-3 gap-8',
    },
    Sidebar: {
      base: 'hidden', // 移动端隐藏侧边栏
      lg: 'block w-64', // 大屏幕显示
    },
    TaskDetail: {
      base: 'fixed inset-0 z-50 bg-white transform translate-x-full transition-transform duration-300',
      expanded: 'translate-x-0', // 移动端全屏模态
      lg: 'static transform-none w-96', // 桌面端固定宽度
    },
  },
  
  // 触摸优化
  touchOptimizations: {
    minTouchTarget: '44px', // 最小触摸目标尺寸
    touchFeedback: 'active:scale-95 transition-transform', // 触摸反馈
    swipeGestures: true, // 支持滑动手势
  },
};

AI生成的响应式组件示例:

// components/task/TaskCard.tsx
import React from 'react';
import { Task } from '../../features/task-management/types';
import { useSwipeable } from 'react-swipeable';
import { motion } from 'framer-motion';

interface TaskCardProps {
  task: Task;
  onComplete: (id: string) => void;
  onEdit: (task: Task) => void;
  onDelete: (id: string) => void;
}

export const TaskCard: React.FC<TaskCardProps> = ({ 
  task, 
  onComplete, 
  onEdit, 
  onDelete 
}) => {
  const [isExpanded, setIsExpanded] = React.useState(false);
  
  // 移动端滑动手势
  const swipeHandlers = useSwipeable({
    onSwipedLeft: () => onDelete(task.id),
    onSwipedRight: () => onComplete(task.id),
    trackMouse: true, // 桌面端也支持
  });
  
  // 优先级颜色映射
  const priorityColors = {
    low: 'bg-blue-100 text-blue-800',
    medium: 'bg-yellow-100 text-yellow-800',
    high: 'bg-orange-100 text-orange-800',
    urgent: 'bg-red-100 text-red-800',
  };
  
  return (
    <motion.div
      {...swipeHandlers}
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, x: -100 }}
      className={`
        bg-white rounded-lg shadow-sm border border-gray-200 
        hover:shadow-md transition-shadow duration-200
        overflow-hidden
        touch-manipulation /* 优化触摸响应 */
      `}
    >
      {/* 移动端:滑动提示 */}
      <div className="lg:hidden flex items-center justify-between px-4 py-2 bg-gray-50 text-sm text-gray-500">
        <span>← 右滑完成</span>
        <span>左滑删除 →</span>
      </div>
      
      <div className="p-4">
        {/* 任务头部 */}
        <div className="flex items-start justify-between mb-3">
          <div className="flex-1 min-w-0"> {/* 防止文本溢出 */}
            <h3 className="text-lg font-semibold text-gray-900 truncate">
              {task.title}
            </h3>
            {task.description && (
              <p className="mt-1 text-gray-600 line-clamp-2">
                {task.description}
              </p>
            )}
          </div>
          
          {/* 优先级标签 - 移动端隐藏,桌面端显示 */}
          <span className={`
            hidden lg:inline-flex px-2 py-1 rounded-full text-xs font-medium
            ${priorityColors[task.priority]}
          `}>
            {task.priority}
          </span>
        </div>
        
        {/* 元信息 */}
        <div className="flex flex-wrap gap-2 mb-4">
          {/* 移动端:简化显示 */}
          <div className="lg:hidden flex items-center space-x-2">
            {task.dueDate && (
              <span className="text-sm text-gray-500">
                📅 {new Date(task.dueDate).toLocaleDateString()}
              </span>
            )}
            <span className={`lg:hidden px-2 py-0.5 rounded-full text-xs ${priorityColors[task.priority]}`}>
              {task.priority}
            </span>
          </div>
          
          {/* 桌面端:完整显示 */}
          <div className="hidden lg:flex flex-wrap gap-2">
            {task.dueDate && (
              <span className="inline-flex items-center px-2 py-1 rounded-md bg-gray-100 text-gray-700 text-sm">
                <CalendarIcon className="w-4 h-4 mr-1" />
                {new Date(task.dueDate).toLocaleDateString()}
              </span>
            )}
            
            {task.estimatedDuration && (
              <span className="inline-flex items-center px-2 py-1 rounded-md bg-blue-50 text-blue-700 text-sm">
                <ClockIcon className="w-4 h-4 mr-1" />
                {task.estimatedDuration}min
              </span>
            )}
            
            {task.tags.map(tag => (
              <span 
                key={tag}
                className="inline-flex items-center px-2 py-1 rounded-md bg-purple-50 text-purple-700 text-sm"
              >
                #{tag}
              </span>
            ))}
          </div>
        </div>
        
        {/* 操作按钮 - 响应式布局 */}
        <div className="flex flex-col sm:flex-row gap-2">
          <button
            onClick={() => onComplete(task.id)}
            className="
              flex-1 px-4 py-2 bg-green-600 text-white rounded-lg
              hover:bg-green-700 active:scale-95 transition-all
              text-sm font-medium
              min-h-[44px] /* 触摸友好 */
            "
            aria-label={`标记任务"${task.title}"为完成`}
          >
            完成
          </button>
          
          <button
            onClick={() => onEdit(task)}
            className="
              flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg
              hover:bg-blue-700 active:scale-95 transition-all
              text-sm font-medium
              min-h-[44px]
            "
            aria-label={`编辑任务"${task.title}"`}
          >
            编辑
          </button>
          
          {/* 移动端:删除按钮隐藏(使用滑动手势替代) */}
          <button
            onClick={() => onDelete(task.id)}
            className="
              hidden lg:flex flex-1 px-4 py-2 bg-red-600 text-white rounded-lg
              hover:bg-red-700 active:scale-95 transition-all
              text-sm font-medium
              min-h-[44px]
            "
            aria-label={`删除任务"${task.title}"`}
          >
            删除
          </button>
        </div>
        
        {/* 展开详情 - 移动端点击展开,桌面端常驻 */}
        <button
          onClick={() => setIsExpanded(!isExpanded)}
          className="
            w-full mt-3 py-2 text-center text-gray-500 text-sm
            hover:text-gray-700 lg:hidden
          "
        >
          {isExpanded ? '收起详情' : '查看更多'}
        </button>
        
        {/* 详情区域 */}
        {(isExpanded || window.innerWidth >= 1024) && task.notes && (
          <div className="mt-3 pt-3 border-t border-gray-200">
            <h4 className="text-sm font-medium text-gray-700 mb-2">备注</h4>
            <p className="text-gray-600 text-sm">{task.notes}</p>
          </div>
        )}
      </div>
    </motion.div>
  );
};

// 图标组件
const CalendarIcon = ({ className }: { className?: string }) => (
  <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
  </svg>
);

const ClockIcon = ({ className }: { className?: string }) => (
  <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
  </svg>
);

5.3 性能监控与错误追踪

生产级应用需要完善的监控。让我们看看如何用规范驱动的方式集成监控系统。

监控需求规范:

功能:应用性能监控与错误追踪
要求:
1. 集成Sentry用于错误追踪
2. 使用Web Vitals监控核心性能指标
3. 自定义业务指标监控(如任务创建成功率、解析准确率)
4. 用户行为分析(匿名)
5. 离线错误队列和重试机制

AI生成的监控集成代码:

// lib/monitoring/setup.ts
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import { onCLS, onFID, onFCP, onLCP, onTTFB, Metric } from 'web-vitals';

// 初始化监控
export function initializeMonitoring() {
  if (process.env.NODE_ENV === 'production') {
    // Sentry错误追踪
    Sentry.init({
      dsn: process.env.REACT_APP_SENTRY_DSN,
      integrations: [new BrowserTracing()],
      tracesSampleRate: 0.2, // 采样率
      environment: process.env.NODE_ENV,
      beforeSend(event) {
        // 过滤敏感信息
        if (event.request?.url) {
          event.request.url = event.request.url.replace(/token=([^&]*)/, 'token=***');
        }
        return event;
      },
    });
    
    // Web Vitals性能监控
    const reportWebVitals = (metric: Metric) => {
      Sentry.captureMessage(`Web Vitals: ${metric.name}`, {
        level: metric.name === 'CLS' && metric.value > 0.1 ? 'warning' : 'info',
        extra: {
          id: metric.id,
          name: metric.name,
          value: metric.value,
          rating: metric.rating,
          delta: metric.delta,
          navigationType: metric.navigationType,
        },
      });
      
      // 发送到分析平台
      if (window.gtag) {
        window.gtag('event', metric.name, {
          event_category: 'Web Vitals',
          value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
          event_label: metric.id,
          non_interaction: true,
        });
      }
    };
    
    // 注册Web Vitals监听器
    onCLS(reportWebVitals);
    onFID(reportWebVitals);
    onFCP(reportWebVitals);
    onLCP(reportWebVitals);
    onTTFB(reportWebVitals);
  }
}

// 业务指标监控
export class BusinessMetrics {
  private static instance: BusinessMetrics;
  private offlineQueue: Array<{ name: string; value: number; tags?: Record<string, string> }> = [];
  private isOnline = navigator.onLine;
  
  private constructor() {
    // 监听网络状态
    window.addEventListener('online', () => {
      this.isOnline = true;
      this.flushOfflineQueue();
    });
    
    window.addEventListener('offline', () => {
      this.isOnline = false;
    });
  }
  
  static getInstance(): BusinessMetrics {
    if (!BusinessMetrics.instance) {
      BusinessMetrics.instance = new BusinessMetrics();
    }
    return BusinessMetrics.instance;
  }
  
  // 记录任务创建成功率
  trackTaskCreation(success: boolean, method: 'manual' | 'smart', duration?: number) {
    this.track('task_creation', success ? 1 : 0, {
      method,
      success: String(success),
      ...(duration && { duration_ms: String(duration) }),
    });
  }
  
  // 记录自然语言解析准确率
  trackNLPAccuracy(confidence: number, language: string) {
    this.track('nlp_accuracy', confidence, {
      language,
      confidence_level: confidence > 0.9 ? 'high' : confidence > 0.7 ? 'medium' : 'low',
    });
  }
  
  // 记录用户活跃度
  trackUserActivity(action: string, component: string) {
    this.track('user_activity', 1, {
      action,
      component,
      timestamp: new Date().toISOString(),
    });
  }
  
  // 通用追踪方法
  private track(name: string, value: number, tags?: Record<string, string>) {
    const metric = { name, value, tags };
    
    if (!this.isOnline) {
      // 离线时存入队列
      this.offlineQueue.push(metric);
      this.saveOfflineQueue();
      return;
    }
    
    this.sendMetric(metric);
  }
  
  private async sendMetric(metric: { name: string; value: number; tags?: Record<string, string> }) {
    try {
      // 发送到监控后端
      const response = await fetch('/api/metrics', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ...metric,
          timestamp: new Date().toISOString(),
          session_id: this.getSessionId(),
          user_id: this.getAnonymousUserId(),
        }),
      });
      
      if (!response.ok) {
        throw new Error(`Failed to send metric: ${response.status}`);
      }
    } catch (error) {
      console.warn('Failed to send metric, storing offline:', error);
      this.offlineQueue.push(metric);
      this.saveOfflineQueue();
    }
  }
  
  private flushOfflineQueue() {
    if (this.offlineQueue.length === 0) return;
    
    const queue = [...this.offlineQueue];
    this.offlineQueue = [];
    
    queue.forEach(metric => this.sendMetric(metric));
  }
  
  private saveOfflineQueue() {
    try {
      localStorage.setItem('metrics_queue', JSON.stringify(this.offlineQueue));
    } catch (error) {
      console.error('Failed to save offline queue:', error);
    }
  }
  
  private loadOfflineQueue() {
    try {
      const saved = localStorage.getItem('metrics_queue');
      if (saved) {
        this.offlineQueue = JSON.parse(saved);
      }
    } catch (error) {
      console.error('Failed to load offline queue:', error);
    }
  }
  
  private getSessionId(): string {
    let sessionId = sessionStorage.getItem('session_id');
    if (!sessionId) {
      sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      sessionStorage.setItem('session_id', sessionId);
    }
    return sessionId;
  }
  
  private getAnonymousUserId(): string {
    let userId = localStorage.getItem('anonymous_user_id');
    if (!userId) {
      userId = `user_${Math.random().toString(36).substr(2, 9)}`;
      localStorage.setItem('anonymous_user_id', userId);
    }
    return userId;
  }
}

// React错误边界组件
export class ErrorBoundary extends React.Component<
  { children: React.ReactNode; fallback?: React.ReactNode },
  { hasError: boolean; error?: Error }
> {
  constructor(props: { children: React.ReactNode; fallback?: React.ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // 记录错误到Sentry
    Sentry.captureException(error, {
      extra: {
        componentStack: errorInfo.componentStack,
      },
    });
    
    // 记录业务指标
    BusinessMetrics.getInstance().track('react_error', 1, {
      name: error.name,
      message: error.message.substring(0, 100),
    });
  }
  
  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="min-h-screen flex items-center justify-center bg-gray-50">
          <div className="text-center">
            <h2 className="text-2xl font-bold text-gray-900 mb-2">出错了</h2>
            <p className="text-gray-600 mb-4">
              抱歉,应用遇到了问题。我们已经记录了这个错误。
            </p>
            <button
              onClick={() => window.location.reload()}
              className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
            >
              重新加载
            </button>
          </div>
        </div>
      );
    }
    
    return this.props.children;
  }
}

// 使用示例
export function withMonitoring<P extends object>(
  WrappedComponent: React.ComponentType<P>
) {
  return function WithMonitoring(props: P) {
    const metrics = BusinessMetrics.getInstance();
    
    // 记录组件渲染
    React.useEffect(() => {
      const componentName = WrappedComponent.displayName || WrappedComponent.name || 'Unknown';
      metrics.trackUserActivity('component_mount', componentName);
      
      return () => {
        metrics.trackUserActivity('component_unmount', componentName);
      };
    }, []);
    
    return <WrappedComponent {...props} />;
  };
}

5.4 渐进式Web应用(PWA)集成

为了让任务管理应用支持离线使用,我们可以将其构建为PWA。

PWA需求规范:

功能:渐进式Web应用支持
要求:
1. 支持离线访问核心功能
2. 可安装到主屏幕
3. 后台同步(当网络恢复时同步数据)
4. 推送通知(任务提醒)
5. 响应式设计适配各种设备

AI生成的Service Worker和PWA配置:

// public/service-worker.js
const CACHE_NAME = 'taskflow-v1';
const OFFLINE_URL = '/offline.html';

// 需要缓存的资源
const PRECACHE_RESOURCES = [
  '/',
  '/index.html',
  '/manifest.json',
  '/static/js/main.js',
  '/static/css/main.css',
  '/icons/icon-192x192.png',
  '/icons/icon-512x512.png',
];

// 安装事件
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => {
        console.log('缓存核心资源');
        return cache.addAll(PRECACHE_RESOURCES);
      })
      .then(() => {
        console.log('Service Worker安装完成');
        return self.skipWaiting();
      })
  );
});

// 激活事件
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            console.log('删除旧缓存:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => {
      console.log('Service Worker激活完成');
      return self.clients.claim();
    })
  );
});

// 获取事件 - 网络优先,回退到缓存
self.addEventListener('fetch', (event) => {
  // 跳过非GET请求和chrome扩展
  if (event.request.method !== 'GET' || 
      event.request.url.startsWith('chrome-extension://')) {
    return;
  }
  
  // API请求使用网络优先策略
  if (event.request.url.includes('/api/')) {
    event.respondWith(
      fetch(event.request)
        .then((response) => {
          // 克隆响应以缓存
          const responseToCache = response.clone();
          caches.open(CACHE_NAME)
            .then((cache) => {
              cache.put(event.request, responseToCache);
            });
          return response;
        })
        .catch(() => {
          // 网络失败时尝试从缓存获取
          return caches.match(event.request);
        })
    );
    return;
  }
  
  // 静态资源使用缓存优先策略
  event.respondWith(
    caches.match(event.request)
      .then((cachedResponse) => {
        if (cachedResponse) {
          // 同时发起网络请求更新缓存
          const fetchPromise = fetch(event.request)
            .then((networkResponse) => {
              // 更新缓存
              caches.open(CACHE_NAME)
                .then((cache) => {
                  cache.put(event.request, networkResponse.clone());
                });
              return networkResponse;
            })
            .catch(() => cachedResponse); // 网络失败使用缓存
        
          return cachedResponse;
        }
        
        // 没有缓存,发起网络请求
        return fetch(event.request)
          .then((response) => {
            // 检查响应是否有效
            if (!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }
            
            // 克隆响应以缓存
            const responseToCache = response.clone();
            caches.open(CACHE_NAME)
              .then((cache) => {
                cache.put(event.request, responseToCache);
              });
            
            return response;
          })
          .catch(() => {
            // 网络失败且没有缓存,显示离线页面
            if (event.request.mode === 'navigate') {
              return caches.match(OFFLINE_URL);
            }
            return new Response('网络连接失败', {
              status: 408,
              headers: { 'Content-Type': 'text/plain' },
            });
          });
      })
  );
});

// 后台同步
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-tasks') {
    event.waitUntil(syncTasks());
  }
});

async function syncTasks() {
  try {
    const db = await openTaskDatabase();
    const pendingTasks = await db.getAll('pending_tasks');
    
    for (const task of pendingTasks) {
      try {
        const response = await fetch('/api/tasks', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(task.data),
        });
        
        if (response.ok) {
          await db.delete('pending_tasks', task.id);
          console.log(`任务 ${task.id} 同步成功`);
          
          // 发送通知
          self.registration.showNotification('同步完成', {
            body: `任务"${task.data.title}"已同步到服务器`,
            icon: '/icons/icon-192x192.png',
          });
        }
      } catch (error) {
        console.error(`任务 ${task.id} 同步失败:`, error);
      }
    }
  } catch (error) {
    console.error('后台同步失败:', error);
  }
}

// 推送通知
self.addEventListener('push', (event) => {
  if (!event.data) return;
  
  const data = event.data.json();
  const options = {
    body: data.body || '您有新的任务提醒',
    icon: '/icons/icon-192x192.png',
    badge: '/icons/badge-72x72.png',
    vibrate: [200, 100, 200],
    data: {
      url: data.url || '/',
    },
    actions: data.actions || [
      { action: 'view', title: '查看' },
      { action: 'dismiss', title: '忽略' },
    ],
  };
  
  event.waitUntil(
    self.registration.showNotification(data.title || '任务提醒', options)
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  
  if (event.action === 'view') {
    event.waitUntil(
      clients.openWindow(event.notification.data.url)
    );
  }
});

// IndexedDB用于离线存储
function openTaskDatabase() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('TaskFlowDB', 1);
    
    request.onerror = () => reject(request.error);
    request.onsuccess = () => resolve(request.result);
    
    request.onupgradeneeded = (event) => {
      const db = event.target.result;
      
      // 创建待同步任务存储
      if (!db.objectStoreNames.contains('pending_tasks')) {
        const store = db.createObjectStore('pending_tasks', { keyPath: 'id' });
        store.createIndex('createdAt', 'createdAt', { unique: false });
      }
      
      // 创建离线任务存储
      if (!db.objectStoreNames.contains('offline_tasks')) {
        const store = db.createObjectStore('offline_tasks', { keyPath: 'id' });
        store.createIndex('dueDate', 'dueDate', { unique: false });
        store.createIndex('priority', 'priority', { unique: false });
      }
    };
  });
}
// public/manifest.json
{
  "name": "TaskFlow Pro",
  "short_name": "TaskFlow",
  "description": "智能任务管理应用,支持自然语言输入和离线使用",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3b82f6",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ],
  "screenshots": [
    {
      "src": "/screenshots/mobile.png",
      "sizes": "375x812",
      "type": "image/png",
      "form_factor": "narrow"
    },
    {
      "src": "/screenshots/tablet.png",
      "sizes": "768x1024",
      "type": "image/png",
      "form_factor": "wide"
    }
  ],
  "categories": ["productivity", "utilities"],
  "shortcuts": [
    {
      "name": "添加新任务",
      "short_name": "添加",
      "description": "快速创建新任务",
      "url": "/new-task"
    },
    {
      "name": "查看今日任务",
      "short_name": "今日",
      "description": "查看今天的任务列表",
      "url": "/today"
    }
  ]
}

通过这些高级示例,你可以看到Claude Code Spec Workflow如何帮助处理复杂的开发场景。从状态管理到响应式设计,从性能监控到PWA集成,规范驱动的方法都能提供系统化的指导,确保AI生成的代码不仅功能完整,而且符合生产级质量标准。

在实际项目中,我通常会将这种复杂功能分解为多个规范阶段,每个阶段都有明确的验收标准。比如PWA集成,我会先做基础Service Worker,然后逐步添加离线存储、后台同步、推送通知等功能。每个阶段完成后都进行测试,确保一切正常工作后再进入下一阶段。

这种迭代式的规范驱动开发,结合AI的代码生成能力,让我能够在保持高质量的同时,大幅提升开发效率。最重要的是,整个过程是可重复、可预测的——这对于团队协作和项目维护来说,价值无法估量。

更多推荐