前端开发编码规范
目录
- 项目结构规范
- 命名规范
- [Vue3 编码规范](#Vue3 编码规范 "#vue3-%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83")
- [TypeScript 规范](#TypeScript 规范 "#typescript-%E8%A7%84%E8%8C%83")
- [CSS/SCSS 规范](#CSS/SCSS 规范 "#cssscss-%E8%A7%84%E8%8C%83")
- [Git 提交规范](#Git 提交规范 "#git-%E6%8F%90%E4%BA%A4%E8%A7%84%E8%8C%83")
- 代码质量规范
项目结构规范
目录结构
csharp
src/
├── api/ # API 接口
│ ├── index.ts # API 统一导出
│ ├── login.ts # 登录相关接口
│ └── system.ts # 系统相关接口
├── assets/ # 静态资源
│ ├── images/ # 图片资源
│ ├── icons/ # 图标资源
│ └── styles/ # 样式资源
├── components/ # 公共组件
│ ├── index.ts # 组件统一导出
│ └── common/ # 通用组件
├── hooks/ # 组合式函数
│ └── index.ts # hooks 统一导出
├── layout/ # 布局组件
├── router/ # 路由配置
│ ├── index.ts # 路由主文件
│ └── routes.ts # 路由配置
├── store/ # 状态管理
│ ├── index.ts # store 主文件
│ └── modules/ # store 模块
├── styles/ # 全局样式
│ ├── index.scss # 样式入口
│ ├── var.css # CSS 变量
│ └── theme.scss # 主题样式
├── types/ # 类型定义
├── utils/ # 工具函数
│ ├── index.ts # 工具函数统一导出
│ ├── request.ts # 请求封装
│ └── validator.ts # 验证工具
└── views/ # 页面组件
├── index/ # 首页
├── login/ # 登录页
└── detail/ # 详情页
文件命名规范
- 组件文件 : 使用 PascalCase,如
UserProfile.vue
- 页面文件 : 使用 kebab-case,如
user-detail.vue
- 工具文件 : 使用 camelCase,如
formatDate.ts
- 样式文件 : 使用 kebab-case,如
user-profile.scss
- 类型文件 : 使用 camelCase,如
userTypes.ts
命名规范
变量命名
typescript
// ✅ 正确
const userName = 'John'
const userList = []
const isLoading = false
const API_BASE_URL = 'https://api.example.com'
// ❌ 错误
const user_name = 'John'
const userlist = []
const is_loading = false
const apiBaseUrl = 'https://api.example.com'
函数命名
typescript
// ✅ 正确
const getUserInfo = () => {}
const handleClick = () => {}
const formatDate = (date: Date) => {}
const validateEmail = (email: string) => {}
// ❌ 错误
const get_user_info = () => {}
const click = () => {}
const date = (date: Date) => {}
const email = (email: string) => {}
组件命名
vue
<!-- ✅ 正确 -->
<script setup lang="ts">
// 组件名使用 PascalCase
defineOptions({
name: 'UserProfile'
})
</script>
<!-- ❌ 错误 -->
<script setup lang="ts">
defineOptions({
name: 'user-profile'
})
</script>
Vue3 编码规范
组件结构
vue
<template>
<!-- 模板内容 -->
</template>
<script setup lang="ts">
// 1. 导入语句
import { ref, computed, onMounted } from 'vue'
import type { PropType } from 'vue'
// 2. 类型定义
interface User {
id: number
name: string
email: string
}
// 3. Props 定义
interface Props {
user: User
loading?: boolean
}
const props = withDefaults(defineProps<Props>(), {
loading: false
})
// 4. Emits 定义
interface Emits {
update: [user: User]
delete: [id: number]
}
const emit = defineEmits<Emits>()
// 5. 响应式数据
const userName = ref('')
const userList = ref<User[]>([])
// 6. 计算属性
const filteredUsers = computed(() => {
return userList.value.filter(user =>
user.name.includes(userName.value)
)
})
// 7. 方法定义
const handleUpdate = (user: User) => {
emit('update', user)
}
const handleDelete = (id: number) => {
emit('delete', id)
}
// 8. 生命周期
onMounted(() => {
// 初始化逻辑
})
// 9. 暴露给父组件的方法
defineExpose({
refresh: () => {
// 刷新逻辑
}
})
</script>
<style lang="scss" scoped>
/* 样式内容 */
</style>
Props 规范
vue
<script setup lang="ts">
// ✅ 推荐:使用 TypeScript 接口定义
interface Props {
title: string
count?: number
items: string[]
user: {
id: number
name: string
}
}
const props = withDefaults(defineProps<Props>(), {
count: 0
})
// ❌ 不推荐:使用 PropType
const props = defineProps({
title: String,
count: {
type: Number,
default: 0
}
})
</script>
事件处理
vue
<template>
<!-- ✅ 正确:使用 handle 前缀 -->
<button @click="handleClick">点击</button>
<input @input="handleInput" />
<form @submit="handleSubmit">提交</form>
<!-- ❌ 错误:直接使用动词 -->
<button @click="click">点击</button>
<input @input="input" />
</template>
<script setup lang="ts">
const handleClick = () => {
// 处理点击事件
}
const handleInput = (event: Event) => {
// 处理输入事件
}
const handleSubmit = (event: Event) => {
// 处理提交事件
}
</script>
响应式数据
vue
<script setup lang="ts">
// ✅ 正确:使用 ref 和 reactive
const count = ref(0)
const user = reactive({
name: '',
email: ''
})
// ✅ 正确:使用 computed
const doubleCount = computed(() => count.value * 2)
// ✅ 正确:使用 watch
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// ❌ 错误:直接修改 props
const updateProps = () => {
props.title = 'new title' // 错误!
}
</script>
TypeScript 规范
类型定义
typescript
// ✅ 正确:使用 interface 定义对象类型
interface User {
id: number
name: string
email: string
age?: number
}
// ✅ 正确:使用 type 定义联合类型
type Status = 'pending' | 'success' | 'error'
type UserRole = 'admin' | 'user' | 'guest'
// ✅ 正确:使用 enum 定义枚举
enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
DELETED = 'deleted'
}
// ✅ 正确:使用泛型
interface ApiResponse<T> {
code: number
message: string
data: T
}
// ❌ 错误:使用 any
const user: any = { name: 'John' }
函数类型
typescript
// ✅ 正确:函数类型定义
const getUserById = (id: number): Promise<User> => {
return fetch(`/api/users/${id}`).then(res => res.json())
}
// ✅ 正确:回调函数类型
const handleUserUpdate = (callback: (user: User) => void) => {
// 处理逻辑
}
// ✅ 正确:可选参数和默认值
const formatDate = (date: Date, format?: string): string => {
// 格式化逻辑
return date.toISOString()
}
// ❌ 错误:缺少类型注解
const getUser = (id) => {
return fetch(`/api/users/${id}`)
}
类型断言
typescript
// ✅ 正确:使用 as 进行类型断言
const element = document.getElementById('app') as HTMLElement
// ✅ 正确:使用尖括号语法(不推荐,与 JSX 冲突)
const element = <HTMLElement>document.getElementById('app')
// ✅ 正确:使用类型守卫
const isUser = (obj: any): obj is User => {
return obj && typeof obj.id === 'number' && typeof obj.name === 'string'
}
// ❌ 错误:过度使用类型断言
const user = {} as User // 可能导致运行时错误
CSS/SCSS 规范
类名命名
scss
// ✅ 正确:使用 BEM 命名规范
.user-profile {
&__header {
// 用户资料头部
}
&__content {
// 用户资料内容
}
&__footer {
// 用户资料底部
}
&--active {
// 激活状态
}
&--disabled {
// 禁用状态
}
}
// ✅ 正确:使用 kebab-case
.user-list {
.user-item {
.user-name {
// 用户名样式
}
}
}
// ❌ 错误:使用驼峰命名
.userProfile {
.userName {
// 不推荐
}
}
样式组织
scss
// ✅ 正确:按功能组织样式
.user-card {
// 1. 布局属性
display: flex;
flex-direction: column;
gap: 1rem;
// 2. 尺寸属性
width: 100%;
max-width: 400px;
height: auto;
// 3. 间距属性
padding: 1rem;
margin: 0 auto;
// 4. 背景属性
background-color: #fff;
border-radius: 8px;
// 5. 边框属性
border: 1px solid #e0e0e0;
// 6. 字体属性
font-size: 14px;
line-height: 1.5;
color: #333;
// 7. 其他属性
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
// 8. 伪类
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
// 9. 子元素
&__title {
font-weight: bold;
margin-bottom: 0.5rem;
}
&__content {
flex: 1;
}
}
变量使用
scss
// ✅ 正确:使用 CSS 变量
:root {
--primary-color: #1890ff;
--secondary-color: #52c41a;
--text-color: #333;
--border-color: #e0e0e0;
--border-radius: 8px;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
}
.button {
background-color: var(--primary-color);
border-radius: var(--border-radius);
padding: var(--spacing-sm) var(--spacing-md);
}
// ✅ 正确:使用 SCSS 变量
$primary-color: #1890ff;
$secondary-color: #52c41a;
$border-radius: 8px;
.button {
background-color: $primary-color;
border-radius: $border-radius;
}
Git 提交规范
提交信息格式
xml
<type>(<scope>): <subject>
<body>
<footer>
类型说明
feat
: 新功能fix
: 修复 bugdocs
: 文档更新style
: 代码格式调整refactor
: 代码重构perf
: 性能优化test
: 测试相关chore
: 构建过程或辅助工具的变动
示例
bash
# ✅ 正确
feat(user): 添加用户登录功能
fix(api): 修复用户列表接口错误
docs(readme): 更新项目说明文档
style(components): 调整按钮组件样式
refactor(utils): 重构日期格式化函数
perf(images): 优化图片加载性能
test(login): 添加登录组件单元测试
chore(deps): 更新依赖包版本
# ❌ 错误
update code
fix bug
add feature
代码质量规范
代码注释
typescript
// ✅ 正确:函数注释
/**
* 格式化日期为指定格式
* @param date 日期对象
* @param format 格式化字符串,默认为 'YYYY-MM-DD'
* @returns 格式化后的日期字符串
* @example
* formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
* // => '2024-01-01 12:00:00'
*/
const formatDate = (date: Date, format: string = 'YYYY-MM-DD'): string => {
// 实现逻辑
}
// ✅ 正确:复杂逻辑注释
// 根据用户权限过滤菜单项
const filteredMenus = menus.filter(menu => {
// 检查用户是否有访问权限
if (!hasPermission(user.role, menu.permission)) {
return false
}
// 检查菜单是否在用户可见范围内
if (menu.hidden && !user.isAdmin) {
return false
}
return true
})
// ❌ 错误:无意义的注释
// 这是一个变量
const name = 'John'
// 这是一个函数
const getName = () => {
return name
}
错误处理
typescript
// ✅ 正确:使用 try-catch 处理异步错误
const fetchUserData = async (userId: number): Promise<User> => {
try {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.json()
return data
} catch (error) {
console.error('Failed to fetch user data:', error)
throw new Error('获取用户数据失败')
}
}
// ✅ 正确:使用 Result 模式
interface Result<T> {
success: boolean
data?: T
error?: string
}
const getUserData = async (userId: number): Promise<Result<User>> => {
try {
const user = await fetchUserData(userId)
return { success: true, data: user }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '未知错误'
}
}
}
性能优化
vue
<script setup lang="ts">
// ✅ 正确:使用 v-memo 优化列表渲染
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.name]">
{{ item.name }}
</div>
</template>
// ✅ 正确:使用 computed 缓存计算结果
const expensiveValue = computed(() => {
return heavyCalculation(props.data)
})
// ✅ 正确:使用 watchEffect 自动清理副作用
watchEffect((onCleanup) => {
const timer = setInterval(() => {
// 定时器逻辑
}, 1000)
onCleanup(() => {
clearInterval(timer)
})
})
// ✅ 正确:使用 defineAsyncComponent 懒加载组件
const AsyncComponent = defineAsyncComponent(() => import('./HeavyComponent.vue'))
</script>
工具配置
ESLint 配置
json
{
"extends": [
"@vue/eslint-config-typescript",
"@vue/eslint-config-prettier"
],
"rules": {
"vue/multi-word-component-names": "off",
"vue/no-unused-vars": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn"
}
}
Prettier 配置
json
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "avoid"
}
TypeScript 配置
json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
总结
遵循以上编码规范可以:
- 提高代码可读性和可维护性
- 减少团队协作中的沟通成本
- 降低代码出错概率
- 提升开发效率