一点代码规范 自用

前端开发编码规范

目录

  • 项目结构规范
  • 命名规范
  • [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: 修复 bug
  • docs: 文档更新
  • 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
  }
}

总结

遵循以上编码规范可以:

  1. 提高代码可读性和可维护性
  2. 减少团队协作中的沟通成本
  3. 降低代码出错概率
  4. 提升开发效率
相关推荐
卸任6 小时前
Vibe coding工具使用心得
前端·ai编程
brien97 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance7 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写8 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李8 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易8 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马8 小时前
前沿视界
前端
kyriewen8 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易8 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员