⚡ 前端代码生成标准模板与代码规范约束规则

基于 OpenClaw + Claude Code 的端到端研发自动化系统

版本 v1.0.0 | 2026-03-15
🔍

1. 概述

1.1 背景

随着 AI Coding 技术的快速发展,OpenClaw 和 Claude Code 等工具已经能够承担从需求分析到代码生成的全流程自动化任务。本规范旨在建立一套标准化的前端代码生成体系,确保 AI 生成的代码质量、可维护性和一致性。

💡 核心目标

最大化 AI 代码生成覆盖率(目标 80%+),同时通过规范化约束确保代码质量

1.2 适用范围

1.3 AI Coding 工具支持

OpenClaw
v2026.3.7+
Claude Code
Sonnet 4.6 / Opus 4.6
GPT-5.4
高级推理
Gemini 3.1
Flash 版本

2. 前端技术栈规范

2.1 核心技术栈(2026 推荐)

类别 技术选型 版本要求 说明
语言 TypeScript 5.4+ 必须使用严格模式
框架 React 19 / Vue 3.5 / Angular 18 最新稳定版 根据项目需求选择
构建工具 Vite 6 / Turbopack 最新版 优先使用 Vite
包管理 pnpm / bun 最新版 推荐 pnpm
代码检查 ESLint 9 + Biome 最新版 Biome 替代 ESLint+Prettier
类型检查 tsgo (TypeScript Go) Preview 性能提升 10 倍
测试框架 Vitest + Playwright 最新版 单元测试+E2E 测试
✅ 性能优化建议

使用 tsgo(TypeScript Go 版本)进行类型检查,速度提升约 10 倍,且能捕获更多类型错误

3. 代码生成标准模板

React 函数组件模板(标准版)

import React, { useState, useEffect, useCallback, useMemo } from 'react';
import type { FC, PropsWithChildren } from 'react';
import styles from './ComponentName.module.css';

// ============ Types ============
export interface ComponentNameProps {
  /** 组件标题 */
  title: string;
  /** 是否显示 */
  visible?: boolean;
  /** 点击回调 */
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  /** 自定义类名 */
  className?: string;
  /** 子元素 */
  children?: React.ReactNode;
}

// ============ Constants ============
const DEFAULT_VISIBLE = true;
const COMPONENT_DISPLAY_NAME = 'ComponentName';

/**
 * ComponentName 组件描述
 * @example <ComponentName title="示例标题" visible={true} />
 */
export const ComponentName: FC<ComponentNameProps> = ({
  title,
  visible = DEFAULT_VISIBLE,
  onClick,
  className = '',
  children,
}) => {
  // ============ State ============
  const [internalState, setInternalState] = useState<boolean>(false);

  // ============ Effects ============
  useEffect(() => {
    console.log(`${COMPONENT_DISPLAY_NAME} mounted`);
    return () => {
      console.log(`${COMPONENT_DISPLAY_NAME} unmounted`);
    };
  }, []);

  // ============ Handlers ============
  const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
    onClick?.(event);
  }, [onClick]);

  // ============ Memoized Values ============
  const processedTitle = useMemo(() => {
    return title.trim();
  }, [title]);

  // ============ Render ============
  if (!visible) {
    return null;
  }

  return (
    <div className={`${styles.container} ${className}`} data-testid="component-name">
      <h2 className={styles.title}>{processedTitle}</h2>
      <button 
        className={styles.button}
        onClick={handleClick}
        type="button"
        aria-label={`Click ${processedTitle}`}
      >
        {children || 'Click Me'}
      </button>
    </div>
  );
};

ComponentName.displayName = COMPONENT_DISPLAY_NAME;
export default ComponentName;

Vue Composition API 模板

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import type { Ref } from 'vue';

// ============ Types ============
export interface ComponentNameProps {
  title: string;
  visible?: boolean;
  items?: Array<{ id: number; name: string }>;
}

// ============ Props ============
const props = withDefaults(defineProps<ComponentNameProps>(), {
  visible: true,
  items: () => [],
});

// ============ Emits ============
const emit = defineEmits<{
  click: [event: MouseEvent];
  update: [value: string];
}>();

// ============ Reactive State ============
const internalCount: Ref<number> = ref(0);
const isLoading = ref(false);

// ============ Computed ============
const processedTitle = computed(() => props.title.trim());
const hasItems = computed(() => props.items.length > 0);

// ============ Lifecycle Hooks ============
onMounted(() => {
  console.log('Component mounted');
  initialize();
});

onUnmounted(() => {
  console.log('Component unmounted');
  cleanup();
});

// ============ Functions ============
function handleClick(event: MouseEvent): void {
  internalCount.value++;
  emit('click', event);
}
</script>

<template>
  <div v-if="visible" class="component-container">
    <h2 class="component-title">{{ processedTitle }}</h2>
    <button @click="handleClick">Click Me</button>
  </div>
</template>

<style module scoped>
.component-container {
  padding: 1rem;
  border-radius: 8px;
}
</style>

自定义 Hook 模板

import { useState, useEffect, useCallback, useRef } from 'react';

export interface UseFetchOptions<T> {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  body?: unknown;
  immediate?: boolean;
  onSuccess?: (data: T) => void;
  onError?: (error: Error) => void;
}

export interface UseFetchReturn<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
  execute: (overrideOptions?: Partial<UseFetchOptions<T>>) => Promise<void>;
  reset: () => void;
}

export function useFetch<T>(options: UseFetchOptions<T>): UseFetchReturn<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<Error | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
  const mountedRef = useRef<boolean>(false);

  useEffect(() => {
    mountedRef.current = true;
    return () => { mountedRef.current = false; };
  }, []);

  const execute = useCallback(async () => {
    abortControllerRef.current?.abort();
    abortControllerRef.current = new AbortController();
    
    setLoading(true);
    setError(null);

    try {
      const response = await fetch(options.url, {
        method: options.method,
        signal: abortControllerRef.current.signal,
      });
      
      const result: T = await response.json();
      setData(result);
      options.onSuccess?.(result);
    } catch (err) {
      if (err instanceof Error && err.name !== 'AbortError') {
        setError(err);
        options.onError?.(err);
      }
    } finally {
      setLoading(false);
    }
  }, [options]);

  useEffect(() => {
    if (options.immediate) {
      execute();
    }
  }, [options.immediate, execute]);

  return { data, loading, error, execute, reset: () => {
    setData(null);
    setLoading(false);
    setError(null);
  }};
}

API Service 模板

import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';

export interface ApiResponse<T = unknown> {
  code: number;
  message: string;
  data: T;
  timestamp: number;
}

const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
const TIMEOUT = 30000;

const apiClient: AxiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: TIMEOUT,
  headers: { 'Content-Type': 'application/json' },
});

// Request Interceptor
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('access_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  config.headers['X-Request-ID'] = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  return config;
});

// Response Interceptor
apiClient.interceptors.response.use(
  (response) => {
    const { data } = response;
    if (data.code !== 200) {
      return Promise.reject(new Error(data.message));
    }
    return data;
  },
  async (error) => {
    const status = error.response?.status;
    if (status === 401) {
      // Handle unauthorized
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export const api = {
  get: <T>(url: string, config?: AxiosRequestConfig) => 
    apiClient.get<ApiResponse<T>>(url, config),
  post: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => 
    apiClient.post<ApiResponse<T>>(url, data, config),
  put: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => 
    apiClient.put<ApiResponse<T>>(url, data, config),
  delete: <T>(url: string, config?: AxiosRequestConfig) => 
    apiClient.delete<ApiResponse<T>>(url, config),
};

export default api;

单元测试模板

import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import userEvent from '@testing-library/user-event';
import ComponentName from './ComponentName';

describe('ComponentName', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should render correctly with required props', () => {
    render(<ComponentName title="Test Title" />);
    expect(screen.getByText('Test Title')).toBeInTheDocument();
  });

  it('should not render when visible is false', () => {
    render(<ComponentName title="Test" visible={false} />);
    expect(screen.queryByTestId('component-name')).not.toBeInTheDocument();
  });

  it('should call onClick when button is clicked', async () => {
    const handleClick = vi.fn();
    render(<ComponentName title="Test" onClick={handleClick} />);
    
    await userEvent.click(screen.getByRole('button'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('should have proper ARIA attributes', () => {
    render(<ComponentName title="Accessible" />);
    expect(screen.getByRole('button'))
      .toHaveAttribute('aria-label', 'Click Accessible');
  });
});

4. 代码规范约束规则

4.1 命名规范

✅ 文件和文件夹命名
components/UserProfile.tsx          # ✅ 组件文件 PascalCase
hooks/useFetchData.ts                 # ✅ hook 文件 camelCase
services/userService.ts               # ✅ 服务文件 camelCase
types/user.types.ts                   # ✅ 类型文件 kebab-case
__tests__/UserProfile.test.tsx        # ✅ 测试文件 .test.tsx
user-profile.module.css               # ✅ 样式文件 kebab-case
❌ 错误示例
components/userProfile.tsx            # ❌ 组件文件应 PascalCase
hooks/usefetchdata.ts                 # ❌ hook 应 camelCase
Services/UserService.ts               # ❌ 服务应 camelCase
Types/UserTypes.ts                    # ❌ 类型文件应 kebab-case

4.2 TypeScript 规范

4.3 代码组织规范

导入顺序: React → 第三方库 → 类型定义 → 样式 → 内部模块 → 相对导入

组件内部顺序: Props → State → Refs → Effects → Handlers → Memoized → Render

5. AI Coding Prompt 工程规范

5.1 Prompt 结构模板

📋 完整 Prompt 结构
  1. Role (角色定义) - 明确 AI 的角色和专业领域
  2. Context (上下文) - 提供项目背景和技术栈信息
  3. Task (任务描述) - 清晰描述需要完成的任务
  4. Constraints (约束条件) - 列出必须遵守的规则和限制
  5. Examples (示例参考) - 提供参考示例帮助理解
  6. Output Format (输出格式) - 指定期望的输出格式

5.2 Prompt 优化技巧

"创建一个表单组件"

问题:过于模糊,缺少具体要求

创建一个用户注册表单组件,包含以下字段:
- 用户名(必填,3-20 个字符,只能包含字母数字下划线)
- 邮箱(必填,需要邮箱格式验证)
- 密码(必填,最少 8 位,包含大小写字母和数字)
- 确认密码(必填,必须与密码一致)
- 同意条款(必填复选框)

技术要求:
- 使用 React Hook Form 进行表单管理
- 使用 Zod 进行 schema 验证
- 实时验证(onBlur)
- 提交时显示加载状态
- 成功后显示 Toast 通知
Step 1: "首先,请定义用户注册表单的数据类型和验证 schema"
Step 2: "基于上面的类型定义,创建表单组件的基础结构"
Step 3: "现在添加表单提交、验证和错误处理逻辑"
Step 4: "最后,为这个组件编写完整的单元测试"

6. 项目结构规范

6.1 标准项目结构

project-root/
├── src/
│   ├── assets/              # 资源文件
│   ├── components/          # 通用组件
│   │   ├── ui/              # UI 基础组件
│   │   ├── business/        # 业务组件
│   │   └── layouts/         # 布局组件
│   ├── hooks/               # 自定义 Hooks
│   ├── pages/               # 页面组件
│   ├── services/            # API 服务
│   ├── store/               # 状态管理
│   ├── styles/              # 全局样式
│   ├── types/               # 类型定义
│   ├── utils/               # 工具函数
│   └── constants/           # 常量定义
├── tests/                   # 测试文件
│   ├── unit/                # 单元测试
│   ├── integration/         # 集成测试
│   └── e2e/                 # E2E 测试
├── docs/                    # 文档
├── .github/workflows/       # CI/CD
└── package.json

6.2 组件目录结构

ComponentName/
├── index.ts                      # 导出入口
├── ComponentName.tsx             # 组件主文件
├── ComponentName.module.css      # 样式文件
├── ComponentName.test.tsx        # 测试文件
├── ComponentName.stories.tsx     # Storybook 故事
├── types.ts                      # 类型定义
└── __tests__/                    # 额外测试(可选)

7. 组件开发规范

7.1 组件设计原则

✅ 单一职责原则

每个组件只做一件事,复杂组件拆分为多个子组件

7.2 性能优化规范

7.3 无障碍访问规范

8. 状态管理规范

8.1 状态管理选型指南

场景 推荐方案 说明
组件本地状态 useState / useReducer 简单状态用 useState,复杂状态用 useReducer
跨组件共享状态 Zustand / Jotai 轻量级全局状态
服务端状态 TanStack Query / SWR 数据获取、缓存、同步
表单状态 React Hook Form 高性能表单管理
复杂应用状态 Redux Toolkit 大型应用,需要时间旅行调试

9. API 接口集成规范

9.1 API 分层架构

src/
├── services/
│   ├── api.ts              # Axios 实例和拦截器
│   └── interceptors/       # 拦截器逻辑
├── api/
│   ├── user.api.ts         # 用户相关 API
│   ├── auth.api.ts         # 认证相关 API
│   └── product.api.ts      # 产品相关 API
├── hooks/
│   └── useUser.ts          # 数据获取 Hook
└── types/
    └── api.types.ts        # API 类型定义

9.2 React Query 最佳实践

10. 测试规范

10.1 测试金字塔

E2E Tests (10%)

关键用户流程

Integration Tests (20%)

组件/API 集成

Unit Tests (70%)

纯函数/Hooks

10.2 测试覆盖率要求

语句覆盖率: ≥ 90%

分支覆盖率: ≥ 85%

函数覆盖率: ≥ 80%

11. CI/CD 集成规范

11.1 GitHub Actions Pipeline

Push / PR
Lint Check
Type Check
Unit Tests
Build
Deploy

11.2 Docker + K8s 部署

12. 人机协同工作流程

12.1 AI 自动生成流程

需求文档 PRD
AI 技术方案
AI 生成代码
代码审查
自动测试
自动部署
UI 验收

12.2 人工审核检查点

阶段 AI 自动化 人工审核 说明
需求分析 ✅ 需求拆解 ✅ 需求确认 AI 拆解需求,人工确认准确性
技术方案 ✅ 方案草拟 ✅ 方案评审 AI 提供方案,人工审核架构合理性
代码生成 ✅ 代码编写 ⚠️ 抽样审查 AI 生成代码,人工抽查关键逻辑
Code Review ✅ 初步检查 ✅ 深度审查 AI 检查规范,人工审查业务逻辑
测试 ✅ 单元测试 ✅ 集成测试 AI 写单测,人工写集成和 E2E 测试
部署 ✅ 自动部署 ⚠️ 发布审批 AI 自动部署,人工审批生产发布
🎯 OpenClaw + Claude Code 集成配置

通过 .openclaw/config.yml 配置文件,可以实现 AI 自动生成代码、自动运行测试、自动 Code Review,并在关键节点触发人工审核,实现高效的人机协同开发流程。

附录

A. 快速检查清单

代码提交前检查

B. 常用命令

# 开发
pnpm dev              # 启动开发服务器
pnpm lint             # 代码检查
pnpm type-check       # 类型检查
pnpm test             # 运行测试
pnpm test:coverage    # 测试覆盖率

# 构建
pnpm build            # 生产构建
pnpm preview          # 预览构建结果

# Docker
docker build -t frontend .
docker-compose up

# K8s
kubectl apply -f k8s/
kubectl get pods
kubectl logs -f deployment/frontend