9.6 KiB
9.6 KiB
前端代码风格与架构规范(Frontend Code Style)
本文档面向 TraceStudio 前端开发者与 AI 助手,定义在 web/ 下的代码风格、目录与职责分配、面向对象与函数式的使用建议、测试规范、以及代码评审/提交约定。目标是:保持一致性、可读性、可维护性,并支持节点式分析平台的扩展性。
一、总体原则
- 以可组合(composition)为首选:优先使用函数式 React 组件 + Hooks;避免复杂继承结构。面向对象用于领域模型、服务与工具(非 React UI)。
- 协议优先于实现:优先定义接口(TypeScript types / interfaces),节点、API 与组件通过契约(schema)通信而非耦合实现细节。
- 原子化与单一职责:组件应尽量小、单一、易测试。复杂视图由小组件组合而成(Container/Presentational 分离或 Hook 提取)。
- 明确分层:UI 组件(pure)→ 容器/页面(组合)→ 状态层(Zustand)→ API 层(
src/utils/api.ts)→ 后端。 - 防御性编程:输入校验、错误边界(Error Boundary)、与用户友好的错误提示。
- 可测试优先:关键逻辑与交互需附带单元/集成测试。
二、技术栈约定(回顾)
- React 19 + TypeScript 5.x(强制开启
strict) - Zustand(全局/共享状态)
- React Flow(节点画布)
- Vite(构建)
- Tailwind CSS(样式) + 可选 shadcn/ui 风格组件
- 测试:Vitest + React Testing Library;E2E 推荐 Playwright
- Lint/格式化:ESLint + Prettier + TypeScript ESLint rules
三、目录与职责(放哪里?)
在 web/src/ 下按以下约定组织文件:
components/:可复用的 UI 组件(按功能或页面子目录组织)- 每个组件使用独立目录:
components/MyComp/包含index.tsx、MyComp.test.tsx、styles.css(如需)、types.ts(组件私有 types)
- 每个组件使用独立目录:
components/nodes/:React Flow 的节点自定义组件(每个节点一个文件)stores/:Zustand store(按 domain 分文件,如useStore.ts,userStore.ts)utils/:工具函数、类型转换、api.ts(与后端请求封装)、nodeRegistry.ts(节点注册逻辑)pages/或views/:高阶页面/路由级容器(如 Workspace 页面)hooks/:自定义 Hook(业务通用逻辑,便于复用)assets/:静态资源(图标、示例文件)styles/:全局样式与 Tailwind 配置(tailwind.css)tests/:大规模集成或 e2e 脚本(可选)types/:全局共享类型定义(如node-types.ts、api-types.ts)
规则举例:
-
组件目录示例:
src/components/FileExplorer/index.tsxsrc/components/FileExplorer/FileExplorer.test.tsxsrc/components/FileExplorer/types.ts
-
节点组件示例:
src/components/nodes/CSVLoader/index.tsxsrc/components/nodes/CSVLoader/preview.tsx(可选)src/components/nodes/CSVLoader/types.ts
四、命名与导出规范
- 文件名:
kebab-case或PascalCase视语言习惯;组件文件夹使用 PascalCase(MyComponent),文件导出使用index.tsx导出默认组件。 - 组件名:PascalCase,例如
FileExplorer、HeaderBar。 - Type / Interface:后缀使用
PascalCase,接口前缀不强制I(推荐不使用),例如NodeMeta,FileInfo。 - 常量:UPPER_SNAKE_CASE 或 PascalCase 视上下文(环境变量 UPPER,UI 常量 PascalCase)。
- Hooks:以
use前缀,例如useFileLoader。 - Store:以
use前缀,例如useStore、useUserStore。
导出风格:
- 组件默认导出:
export default FileExplorer(便于懒加载) - 类型与工具使用具名导出:
export type { NodeMeta },export function formatDate()
五、组件设计规范(React)
- 优先使用函数组件 + Hooks。
- 组件职责:小、单一、可复用;复杂交互拆为子组件与 Hook。
- Props 类型:显式声明
type Props = { ... }并React.FC<Props>或function Comp(props: Props)。 - 不要在组件内部进行副作用外的复杂业务逻辑——将其抽到 Hook 或 store。
- 尽量无状态(presentational)组件与有状态容器(container)分离。
- 使用
ErrorBoundary包裹可能抛错的区域(如渲染外部数据)并显示用户友好错误。 - 可访问性(a11y):可键盘操作、SVG 加上
aria-*、语义化标签。
示例组件骨架:
// src/components/MyWidget/index.tsx
import React from 'react'
import type { MyWidgetProps } from './types'
export default function MyWidget(props: MyWidgetProps) {
const { title, data } = props
return (
<div className="my-widget">
<h3>{title}</h3>
{/* 渲染 */}
</div>
)
}
六、面向对象(OOP)使用指导
- 在前端工程中,OOP 适用于:
- 业务模型(domain models)和复杂数据结构(例如:TraceFile、ProfileSummary)。
- 服务类(与后端交互的封装,如
class FileService { upload(){} }),特别是当有内部状态/连接需要封装时。
- 禁忌:不要为了使用类而用类来实现 UI 组件(React 已以函数式为主)。
- 推荐模式:小型无状态类 + 通过接口注入(依赖反转),便于在测试中替换实现。
示例(服务类):
// src/utils/FileService.ts
export class FileService {
constructor(private baseUrl: string) {}
async upload(file: File) { /* ... */ }
}
七、状态管理(Zustand)约定
- 所有应用级状态放在
src/stores/,按 domain 拆分(useStore.ts为全局工作流状态,useUserStore.ts单独管理用户信息)。 - Store 只包含状态与同步/异步的变更方法(actions);避免在 store 中做 DOM 操作或复杂渲染逻辑。
- Store 的方法应返回 Promise(如有异步),便于调用方捕获错误与链式处理。
示例:
// src/stores/useStore.ts
export const useStore = create((set, get) => ({
nodes: [],
setNodes: (nodes) => set({ nodes }),
loadWorkflow: async (path) => { /* await fetch... */ }
}))
八、API 层与后端契约
- 所有后端请求通过
src/utils/api.ts封装为高层方法(getPlugins(),executeGraph(payload)等)。 - API 返回应统一为
{ data?: any, error?: string },前端统一解析该结构。 - 在文档中声明关键端点与示例请求/响应(见下文 API 规范草案建议)。
九、节点插件与自定义节点约定
- 节点元数据接口(示例):
export type NodeMetadata = {
display_name: string
category: string
inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }>
params?: Record<string, any>
}
- 前端
nodeRegistry应暴露组件映射与meta,并以GET /api/plugins为数据源。如果用户上传自定义节点并注册,后端应返回插件列表更新。 - 前端对自定义节点仅渲染元数据描述与 UI 表单;执行逻辑由后端驱动或以安全沙箱运行。
十、错误处理与日志
- 所有异步 API 调用必须有错误处理(try/catch),并返回友好错误到 UI(非控制台)。
- 在关键组件使用
try { } catch (err) { setError(err.message) }并在 UI 显示可复制的错误信息和“定位至节点”或“查看详细日志”按钮。
十一、测试规范
- 单元/组件测试:Vitest + React Testing Library
- 文件命名:
Xxx.test.ts/Xxx.test.tsx - 覆盖核心逻辑与交互,包括:NodePalette 渲染、拖拽行为(可模拟 dataTransfer)、Inspector 参数绑定、FileExplorer 文件操作(mock API)。
- 断言样式:优先使用可见文本、ARIA 查询与行为断言(而非实现细节)。
- 文件命名:
- 集成测试:对关键业务流(导入文件 → 创建节点 → 预览 → 运行)编写集成用例。
- E2E:Playwright(建议用于文件拖放、跨窗口文件预览、上传流程)
- 覆盖率门槛:建议核心模块覆盖率 >= 70%,node/plugins 关键逻辑 >= 80%(可视团队资源调整)。
运行命令示例:
# 在 web/ 目录
npm run test # 运行单元测试(vite/vitest)
npm run test:watch # 开发时监视测试
npm run test:e2e # (若配置) 运行 e2e
十二、Lint / 格式化 / 提交规范
- ESLint + Prettier:在 PR 前执行
npm run lint与npm run format。 - 代码风格:开启 TypeScript
strict、避免any(必要时使用注释说明)。 - Commit 信息:使用 Conventional Commits(
feat:、fix:、chore:、docs:等)。 - PR 模板:每个 PR 需包含变更摘要、测试说明、影响范围、回滚/兼容性注意事项。
十三、Code Review Checklist(最少项)
- 代码是否小且聚焦单一职责?
- 有无单元/集成测试覆盖关键路径?
- 类型是否明确(避免 any)?
- 是否保持了 UI/UX 与现有组件风格一致?
- 是否包含必要的错误处理与用户提示?
- 是否有性能或安全问题(例如不受限的文件读写、直接 eval)?
十四、示例:将新功能放在哪儿(快速参考)
- 新增后端 API 的前端支持:
utils/api.ts添加调用方法stores/添加状态/方法components/添加或更新 UIcomponents/nodes/若涉及新节点,添加节点组件并更新nodeRegistry
- 新增 UI 视图:在
pages/新建页面,页面中组合已有组件或新增 container 组件。
十五、附录 - 推荐工具 & VSCode 插件
- ESLint, Prettier, TypeScript, Vitest, Playwright, Tailwind CSS Intellisense
- GitLens, EditorConfig, ESLint plugin for VSCode