前端开发规范
参考来源: Vue 官方风格指南、Element Plus 最佳实践
UI 风格约束
严格禁止(常见 AI 风格)
-
❌ 蓝紫色霓虹渐变、发光描边、玻璃拟态
-
❌ 大面积渐变、过多装饰性几何图形
-
❌ 赛博风、暗黑科技风、AI 风格 UI
-
❌ UI 文案中使用 emoji
后台系统(默认风格)
要素 要求
主题 使用组件库默认主题
配色 黑白灰为主 + 1 个主色点缀
动效 克制,仅保留必要交互反馈
技术栈
层级 Vue(首选) React(备选)
框架 Vue 3 + TypeScript React 18 + TypeScript
构建 Vite Vite
路由 Vue Router 4 React Router 6
状态 Pinia Zustand
UI 库 Element Plus Ant Design
Vue 编码规范
组件基础
<script setup lang="ts"> import { ref, computed, onMounted } from 'vue' import type { User } from '@/types'
// Props & Emits const props = defineProps<{ userId: number }>() const emit = defineEmits<{ (e: 'update', value: string): void }>()
// 响应式状态 const loading = ref(false) const user = ref<User | null>(null)
// 计算属性 const displayName = computed(() => user.value?.name ?? '未知用户')
// 生命周期 onMounted(async () => { await fetchUser() })
// 方法 async function fetchUser() { loading.value = true try { user.value = await api.getUser(props.userId) } finally { loading.value = false } } </script>
<template> <div class="user-card"> <h3>{{ displayName }}</h3> </div> </template>
<style scoped> .user-card { padding: 16px; } </style>
命名约定
类型 约定 示例
组件文件 PascalCase.vue UserCard.vue
Composables useXxx.ts useAuth.ts
Store useXxxStore.ts useUserStore.ts
状态管理(Pinia)
// stores/user.ts export const useUserStore = defineStore('user', () => { const user = ref<User | null>(null) const token = ref<string>('')
const isLoggedIn = computed(() => !!token.value)
async function login(username: string, password: string) { const res = await api.login(username, password) token.value = res.token user.value = res.user }
return { user, token, isLoggedIn, login } })
交互状态处理
必须处理的状态: loading、empty、error、disabled、submitting
<template> <el-skeleton v-if="loading" :rows="5" animated /> <el-result v-else-if="error" icon="error" :title="error"> <template #extra> <el-button @click="fetchData">重试</el-button> </template> </el-result> <el-empty v-else-if="list.length === 0" description="暂无数据" /> <template v-else> <!-- 正常内容 --> </template> </template>
TypeScript 规范
// types/user.ts export interface User { id: number username: string role: 'admin' | 'user' }
export interface ApiResponse<T = unknown> { code: number message: string data: T }
性能优化
场景 方案
大列表 虚拟滚动
路由 懒加载 () => import()
计算 使用 computed 缓存
大数据 使用 shallowRef
// 路由懒加载 const routes = [ { path: '/dashboard', component: () => import('@/views/Dashboard.vue') } ]
// 请求防抖 import { useDebounceFn } from '@vueuse/core' const debouncedSearch = useDebounceFn((keyword) => api.search(keyword), 300)
目录结构
src/ ├── assets/ │ └── styles/ # 全局/共享样式 ├── api/ # API 请求 ├── components/ # 通用组件 ├── composables/ # 组合式函数 ├── router/ # 路由配置 ├── stores/ # Pinia stores ├── types/ # TypeScript 类型 ├── utils/ # 工具函数 ├── views/ # 页面组件 ├── App.vue └── main.ts
样式管理规范
规则 说明
❌ 禁止在 .vue 中写大段样式 <style> 块不超过 20 行
❌ 禁止在 .tsx 中写大段内联样式 样式对象/CSS-in-JS 不超过 20 行
✅ 共享样式抽到 src/assets/styles/
按模块拆分文件
✅ 组件内只保留极简样式 Vue: scoped 微调;React: className 引用
src/assets/styles/ ├── variables.scss # 变量(颜色、间距、字号) ├── common.scss # 通用样式 └── [module].scss # 按模块拆分
请求体完整性规范
规则 说明
❌ 禁止 UI 可选字段未传入 API 用户选择/输入的字段必须全部传入请求体
✅ 提交函数与表单字段一一对应 用 TypeScript interface 约束请求体
// ❌ UI 有支付方式选择器,但请求体没传 payMethod const payMethod = ref<'wechat' | 'points' | 'mixed'>('wechat')
async function createOrder() { await api.createOrder({ items: orderItems.value, addressId: selectedAddress.value.id, // payMethod 忘记传了!支付方式选择 UI 形同虚设 }) }
// ✅ 请求体与 UI 表单字段对应 interface CreateOrderRequest { items: OrderItem[] addressId: number payMethod: 'wechat' | 'points' | 'mixed' // 类型约束确保不遗漏 }
async function createOrder() { const request: CreateOrderRequest = { items: orderItems.value, addressId: selectedAddress.value.id, payMethod: payMethod.value, // TypeScript 会提示缺少字段 } await api.createOrder(request) }
API 错误处理规范
规则 说明
❌ 禁止静默忽略非成功响应 res.code !== 200 时必须提示用户
✅ 统一错误提示 非成功响应统一 message.error 提示
✅ 网络异常也要处理 try/catch 捕获请求异常
// ❌ 只处理成功,非 200 静默忽略 const res = await api.getList(params) if (res.code === 200) { list.value = res.data }
// ✅ 成功 + 失败都处理 try { const res = await api.getList(params) if (res.code === 200) { list.value = res.data } else { message.error(res.message || '加载失败') } } catch (e) { message.error('网络异常,请稍后重试') }
类型复用规范
规则 说明
❌ 禁止多个文件重复定义相同接口 PageResponse 、BaseResult 等
✅ 通用类型统一放 @/types/common.ts
全局导出,各处引用
// ❌ 每个 api 文件都定义一遍 // api/user.ts interface PageResponse<T> { list: T[]; total: number } // api/order.ts interface PageResponse<T> { list: T[]; total: number } // 重复
// ✅ 统一定义,各处引用 // types/common.ts export interface PageResponse<T> { list: T[] total: number }
// api/user.ts import type { PageResponse } from '@/types/common'
详细参考
文件 内容
references/frontend-style.md
UI 风格、Vue 3 规范、Pinia、API 封装、性能优化
references/miniapp-pitfalls.md
uni-app 陷阱:页面栈只读、Storage 清理时机、生命周期双触发、前端校验镜像
references/date-time.md
dayjs/date-fns 日期加减、账期计算、禁止月末对齐
📋 本回复遵循:frontend-dev
- [具体章节]