OPC-Starter 学习教程
OPC-Starter 学习教程
目录
一、快速入门
1.1 环境准备
系统要求: - Node.js 18+ - npm 9+ 或 pnpm 8+ - Git
推荐工具: - Cursor - AI 代码编辑器(推荐) - Qoder - AI 编程助手 - VS Code - 传统编辑器
1.2 克隆项目
# 克隆仓库
git clone https://github.com/alibaba/opc-starter.git
cd opc-starter
# 安装依赖
npm install
1.3 启动开发服务器
方式一:MSW Mock 模式(推荐新手)
无需配置真实后端,使用 Mock 数据:
# 使用测试环境配置启动
npm run dev:test
访问 http://localhost:5173
测试账号:
- 邮箱:test@example.com
- 密码:888888
方式二:真实 Supabase 模式
- 创建 Supabase 项目:https://supabase.com
- 配置环境变量:
# 复制环境变量模板
cp app/.env.example app/.env.local
编辑 app/.env.local:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
-
运行数据库 Schema(在 Supabase SQL Editor 中执行
app/supabase/setup.sql) -
启动应用:
npm run dev
1.4 项目验证
# 类型检查
npm run type-check
# 单元测试
npm test
# 构建
npm run build
# 完整质量检查
npm run ai:check
二、项目结构详解
2.1 目录结构
app/
├── src/
│ ├── auth/ # 认证模块
│ │ ├── AuthProvider.tsx # 认证上下文
│ │ ├── LoginForm.tsx # 登录表单
│ │ └── ProtectedRoute.tsx # 路由守卫
│ │
│ ├── components/ # 组件目录
│ │ ├── agent/ # Agent Studio
│ │ ├── business/ # 业务组件
│ │ ├── layout/ # 布局组件
│ │ ├── organization/ # 组织架构
│ │ └── ui/ # 基础 UI
│ │
│ ├── hooks/ # 自定义 Hooks
│ ├── lib/ # 库封装
│ │ ├── agent/ # Agent 核心
│ │ ├── reactive/ # 响应式数据
│ │ └── supabase/ # Supabase 客户端
│ │
│ ├── pages/ # 页面组件
│ ├── services/ # 服务层
│ │ └── data/ # DataService
│ │
│ ├── stores/ # Zustand 状态
│ ├── types/ # TypeScript 类型
│ └── utils/ # 工具函数
│
├── supabase/
│ ├── functions/ # Edge Functions
│ └── setup.sql # 数据库 Schema
│
├── cypress/ # E2E 测试
└── .env.test # 测试环境变量
2.2 核心入口文件
main.tsx - 应用入口:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'
// MSW Mock 初始化(开发/测试环境)
if (import.meta.env.VITE_ENABLE_MSW === 'true') {
import('./mocks/browser').then(({ worker }) => {
worker.start()
})
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
App.tsx - 根组件:
import { AuthProvider } from '@/auth/AuthProvider'
import { Router } from '@/config/routes'
function App() {
return (
<AuthProvider>
<Router />
</AuthProvider>
)
}
export default App
2.3 路由配置
config/routes.tsx:
import { createBrowserRouter } from 'react-router-dom'
import { ProtectedRoute } from '@/auth/ProtectedRoute'
import { Layout } from '@/components/layout/Layout'
import { Dashboard } from '@/pages/Dashboard'
import { Login } from '@/pages/Login'
export const router = createBrowserRouter([
{
path: '/login',
element: <Login />,
},
{
path: '/',
element: (
<ProtectedRoute>
<Layout />
</ProtectedRoute>
),
children: [
{ index: true, element: <Dashboard /> },
{ path: 'settings', element: <Settings /> },
// 添加更多路由...
],
},
])
三、核心功能开发
3.1 认证系统
登录流程:
// pages/Login.tsx
import { useAuth } from '@/auth/AuthProvider'
import { LoginForm } from '@/auth/LoginForm'
export function Login() {
const { signIn } = useAuth()
const handleSubmit = async (email: string, password: string) => {
const { error } = await signIn(email, password)
if (error) {
// 错误处理
}
}
return <LoginForm onSubmit={handleSubmit} />
}
AuthProvider 实现:
// auth/AuthProvider.tsx
import { createContext, useContext, useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'
interface AuthContextType {
user: User | null
signIn: (email: string, password: string) => Promise<void>
signOut: () => Promise<void>
}
const AuthContext = createContext<AuthContextType | null>(null)
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
// 监听认证状态变化
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_, session) => {
setUser(session?.user ?? null)
}
)
return () => subscription.unsubscribe()
}, [])
const signIn = async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) throw error
}
const signOut = async () => {
await supabase.auth.signOut()
setUser(null)
}
return (
<AuthContext.Provider value={{ user, signIn, signOut }}>
{children}
</AuthContext.Provider>
)
}
export const useAuth = () => {
const context = useContext(AuthContext)
if (!context) throw new Error('useAuth must be used within AuthProvider')
return context
}
3.2 添加新页面
Step 1: 创建页面组件
// pages/Products.tsx
import { useQuery } from '@tanstack/react-query'
import { dataService } from '@/services/data/DataService'
export function Products() {
const { data: products, isLoading } = useQuery({
queryKey: ['products'],
queryFn: () => dataService.getProducts(),
})
if (isLoading) return <div>Loading...</div>
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Products</h1>
<div className="grid grid-cols-3 gap-4">
{products?.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
)
}
Step 2: 添加路由
// config/routes.tsx
import { Products } from '@/pages/Products'
// 在 children 数组中添加
{ path: 'products', element: <Products /> },
Step 3: 添加导航
// components/layout/Sidebar.tsx
const navItems = [
{ path: '/', label: 'Dashboard', icon: Home },
{ path: '/products', label: 'Products', icon: Package },
// ...
]
3.3 状态管理
使用 Zustand:
// stores/productStore.ts
import { create } from 'zustand'
interface ProductState {
products: Product[]
selectedProduct: Product | null
setProducts: (products: Product[]) => void
selectProduct: (product: Product | null) => void
}
export const useProductStore = create<ProductState>((set) => ({
products: [],
selectedProduct: null,
setProducts: (products) => set({ products }),
selectProduct: (product) => set({ selectedProduct: product }),
}))
在组件中使用:
// components/ProductList.tsx
import { useProductStore } from '@/stores/productStore'
export function ProductList() {
const { products, selectProduct } = useProductStore()
return (
<ul>
{products.map((product) => (
<li key={product.id} onClick={() => selectProduct(product)}>
{product.name}
</li>
))}
</ul>
)
}
四、Agent Studio (A2UI)
4.1 A2UI 协议简介
A2UI(Agent-to-User Interface)是一种声明式 UI 协议,让 AI Agent 可以生成动态交互界面。
工作流程:
用户输入 → LLM 解析 → A2UI Schema → 动态渲染
4.2 配置 Agent Studio
环境变量:
# 使用阿里云通义千问
VITE_DASHSCOPE_API_KEY=your_dashscope_api_key
# 或使用其他 LLM Provider
VITE_OPENAI_API_KEY=your_openai_api_key
4.3 使用 Agent Studio
基本用法:
// components/AgentChat.tsx
import { AgentStudio } from '@/components/agent/AgentStudio'
export function AgentChat() {
const handleResponse = (schema: A2UISchema) => {
console.log('AI generated UI:', schema)
}
return (
<div className="h-screen">
<AgentStudio onResponse={handleResponse} />
</div>
)
}
A2UI Schema 示例:
{
"type": "container",
"children": [
{
"type": "text",
"content": "Hello, User!",
"style": { "fontSize": "24px", "fontWeight": "bold" }
},
{
"type": "button",
"label": "Click me",
"onClick": "handleClick"
},
{
"type": "list",
"items": [
{ "type": "text", "content": "Item 1" },
{ "type": "text", "content": "Item 2" }
]
}
]
}
4.4 自定义 A2UI 组件
注册自定义组件:
// lib/agent/customComponents.ts
import { registerA2UIComponent } from '@/lib/agent/registry'
// 注册自定义图表组件
registerA2UIComponent('chart', {
render: (props: ChartProps) => <Chart {...props} />,
validate: (props) => {
// 验证 props
return true
},
})
在 A2UI Schema 中使用:
{
"type": "chart",
"chartType": "line",
"data": [
{ "x": "Jan", "y": 100 },
{ "x": "Feb", "y": 150 }
]
}
五、数据服务层
5.1 DataService 架构
┌─────────────────────────────────────────────────────────┐
│ DataService │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 统一 API 接口 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ IndexedDB │ │ Sync │ │ Supabase │ │
│ │ (本地存储) │ │ (同步引擎) │ │ (云端 API) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
5.2 使用 DataService
读取数据:
import { dataService } from '@/services/data/DataService'
// 获取单个实体
const product = await dataService.getProduct('product-123')
// 获取列表
const products = await dataService.getProducts({ category: 'electronics' })
// 使用 React Query
const { data, isLoading } = useQuery({
queryKey: ['products', category],
queryFn: () => dataService.getProducts({ category }),
})
写入数据:
// 创建
const newProduct = await dataService.createProduct({
name: 'New Product',
price: 99.99,
})
// 更新
await dataService.updateProduct('product-123', {
price: 89.99,
})
// 删除
await dataService.deleteProduct('product-123')
5.3 实时订阅
订阅数据变化:
// hooks/useRealtimeProducts.ts
import { useEffect, useState } from 'react'
import { dataService } from '@/services/data/DataService'
export function useRealtimeProducts() {
const [products, setProducts] = useState<Product[]>([])
useEffect(() => {
// 初始加载
dataService.getProducts().then(setProducts)
// 订阅变化
const subscription = dataService.subscribeToProducts((updated) => {
setProducts(updated)
})
return () => subscription.unsubscribe()
}, [])
return products
}
5.4 离线支持
IndexedDB 缓存:
// services/data/adapters/IndexedDBAdapter.ts
import { openDB } from 'idb'
const db = await openDB('opc-starter', 1, {
upgrade(db) {
db.createObjectStore('products', { keyPath: 'id' })
db.createObjectStore('orders', { keyPath: 'id' })
},
})
// 读取
const products = await db.getAll('products')
// 写入
await db.put('products', product)
// 删除
await db.delete('products', productId)
六、测试与质量保证
6.1 单元测试
使用 Vitest:
// __tests__/components/ProductCard.test.tsx
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { ProductCard } from '@/components/ProductCard'
describe('ProductCard', () => {
it('renders product name', () => {
const product = { id: '1', name: 'Test Product', price: 99.99 }
render(<ProductCard product={product} />)
expect(screen.getByText('Test Product')).toBeInTheDocument()
})
it('formats price correctly', () => {
const product = { id: '1', name: 'Test', price: 99.99 }
render(<ProductCard product={product} />)
expect(screen.getByText('$99.99')).toBeInTheDocument()
})
})
运行测试:
# 运行所有测试
npm test
# 监听模式
npm test -- --watch
# 覆盖率报告
npm test -- --coverage
6.2 E2E 测试
使用 Cypress:
// cypress/e2e/login.cy.ts
describe('Login Flow', () => {
it('should login successfully', () => {
cy.visit('/login')
cy.get('[data-testid="email-input"]').type('test@example.com')
cy.get('[data-testid="password-input"]').type('888888')
cy.get('[data-testid="login-button"]').click()
cy.url().should('include', '/')
cy.contains('Dashboard').should('be.visible')
})
})
运行 E2E 测试:
# 交互模式
npm run test:e2e
# 无头模式(CI)
npm run test:e2e:headless
6.3 质量检查
AI 质量检查命令:
# 核心检查(lint + type-check + test + build)
npm run ai:check
# 完整质量检查脚本
./scripts/quality_check.sh
质量检查清单:
- [ ] ESLint 无错误
- [ ] TypeScript 类型检查通过
- [ ] 单元测试全部通过
- [ ] E2E 测试全部通过
- [ ] 生产构建成功
- [ ] 无 console 警告
七、AI 辅助开发最佳实践
7.1 使用 AGENTS.md
AGENTS.md 是给 AI 编码工具的项目指南:
# AGENTS.md
## 项目概述
OPC-Starter 是一个 AI-Friendly React Boilerplate...
## 代码风格
- 使用函数组件和 Hooks
- 遵循 TypeScript 严格模式
- 组件命名:PascalCase
- 函数命名:camelCase
## 目录约定
- components/ - 可复用组件
- pages/ - 页面组件
- hooks/ - 自定义 Hooks
- stores/ - Zustand 状态
## AI 迭代地图
| 想做什么 | 从哪里开始 | 推荐验证 |
|---------|-----------|---------|
| 新增页面 | routes.tsx | npm run type-check |
| 改数据访问 | DataService.ts | npm test |
...
7.2 Cursor 使用技巧
1. 使用 @ 符号引用文件
@components/ProductCard.tsx 请帮我添加一个价格显示功能
2. 使用 Codebase Indexing
# indexing 整个项目
/Codebase: Re-index
3. 使用 BMAD 工作流
/analyst 分析这个需求
/pm 生成 PRD
/dev 实现功能
7.3 AI 友好的代码结构
原则:
- 单一职责:每个文件只做一件事
- 清晰命名:文件名和函数名要有描述性
- 类型完整:不要使用 any
- 注释适度:复杂逻辑添加注释
示例:
// ❌ 不好:文件太大,职责不清
// components/Product.tsx (500 行)
// ✅ 好:职责清晰
// components/ProductCard.tsx (50 行)
// components/ProductList.tsx (80 行)
// components/ProductDetail.tsx (100 行)
// hooks/useProduct.ts (40 行)
// types/product.ts (20 行)
7.4 常见 AI 辅助任务
添加新功能:
请帮我添加一个订单管理页面:
1. 显示订单列表
2. 支持搜索和筛选
3. 点击订单可查看详情
参考 @components/ProductList.tsx 的结构
修复 Bug:
@components/Checkout.tsx 中的支付按钮点击后没有响应
请帮我定位问题并修复
重构代码:
@services/data/DataService.ts 这个文件太大了
请帮我拆分成多个文件,保持功能不变
八、部署与上线
8.1 构建生产版本
# 构建
npm run build
# 预览构建结果
npm run preview
8.2 部署到 Vercel
方式一:通过 Vercel CLI
# 安装 Vercel CLI
npm i -g vercel
# 部署
vercel
方式二:通过 GitHub 集成
- 将代码推送到 GitHub
- 在 Vercel 中导入项目
- 配置环境变量
- 自动部署
8.3 部署到 Netlify
# 安装 Netlify CLI
npm i -g netlify-cli
# 构建
npm run build
# 部署
netlify deploy --prod --dir=dist
8.4 环境变量配置
生产环境变量:
# Supabase
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-production-anon-key
# LLM Provider(可选)
VITE_DASHSCOPE_API_KEY=your_dashscope_api_key
# 功能开关
VITE_ENABLE_MSW=false
8.5 Supabase 生产配置
安全设置:
- 在 Supabase Dashboard 中配置 Row Level Security (RLS)
- 设置 API 速率限制
- 配置 CORS 允许的域名
数据库优化:
-- 添加索引
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- 配置 RLS 策略
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view their own orders"
ON orders FOR SELECT
USING (user_id = auth.uid());
附录:常用命令速查表
| 命令 | 说明 |
|---|---|
npm run dev |
启动开发服务器(真实后端) |
npm run dev:test |
启动开发服务器(Mock 模式) |
npm test |
运行单元测试 |
npm run test:e2e |
运行 E2E 测试 |
npm run type-check |
TypeScript 类型检查 |
npm run lint |
ESLint 检查 |
npm run build |
构建生产版本 |
npm run ai:check |
完整质量检查 |
./scripts/quality_check.sh |
完整质量检查(含 E2E) |
教程生成时间:2026-03-21