CODING-STANDARDS.md 6.29 KB

代码规范

更新时间: 2026-02-05

本文档定义项目的代码规范。


📋 命名规范

文件命名

组件:      PascalCase     UserCard.vue
页面:      PascalCase     Dashboard/index.vue
API 文件:  camelCase      userAPI.js
工具函数:  camelCase      formatDate.js
常量文件:  UPPER_CASE     API_CONSTANTS.js

变量命名

// ✅ 正确
const userList = ref([])
const isLoading = ref(false)
const MAX_COUNT = 100

// ❌ 错误
const UserList = ref([])
const is_loading = ref(false)
const max_count = 100

函数命名

// ✅ 正确 - 动词开头
const fetchUserData = async () => {}
const handleSubmit = () => {}
const formatDate = (date) => {}

// ❌ 错误
const userData = async () => {}  // 应该是 fetchUserData
const submit = () => {}           // 应该是 handleSubmit

🎯 Vue 3 规范

组件定义

<script setup>
// 1. 导入
import { ref, computed, onMounted } from 'vue'
import { useLoad } from '@tarojs/taro'
import { userAPI } from '@/api/user'

// 2. Props 定义
const props = defineProps({
  userId: {
    type: Number,
    required: true,
    default: 0
  }
})

// 3. Emits 定义
const emit = defineEmits(['update', 'delete'])

// 4. 响应式状态
const loading = ref(false)
const dataList = ref([])

// 5. 计算属性
const totalCount = computed(() => dataList.value.length)

// 6. 方法
const fetchData = async () => {
  loading.value = true
  try {
    const res = await userAPI()
    if (res.code === 1) {
      dataList.value = res.data
    }
  } finally {
    loading.value = false
  }
}

// 7. 生命周期(Taro Hooks)
useLoad(() => {
  fetchData()
})
</script>

Props 验证

// ✅ 正确 - 完整的类型和默认值
const props = defineProps({
  userId: {
    type: Number,
    required: true,
    default: 0
  },
  userName: {
    type: String,
    default: ''
  },
  isActive: {
    type: Boolean,
    default: false
  }
})

// ❌ 错误 - 缺少类型
const props = defineProps(['userId', 'userName'])

Emits 定义

// ✅ 正确 - 定义事件名
const emit = defineEmits({
  // 带验证的事件
  update: (payload) => typeof payload.id === 'number',
  // 无验证的事件
  delete: null
})

// ❌ 错误 - 未定义事件
const emit = defineEmits()

🎨 样式规范

TailwindCSS 优先

<template>
  <!-- ✅ 优先使用 TailwindCSS -->
  <view class="flex items-center justify-between p-4 bg-white">
    <text class="text-xl font-bold text-gray-900">标题</text>
  </view>
</template>

<style lang="less" scoped>
/* ✅ 仅在必要时使用 Less */
.custom-element {
  // 深度选择器
  :deep(.nut-popup) {
    background-color: #fff;
  }

  // 复杂动画
  @keyframes slide-in {
    from { transform: translateX(-100%); }
    to { transform: translateX(0); }
  }
}
</style>

样式作用域

<!-- ✅ 页面组件必须使用 scoped -->
<template>
  <view class="page-container">...</view>
</template>

<style lang="less" scoped>
.page-container {
  padding: 30px;
}
</style>

📡 API 调用规范

统一错误处理

// ✅ 正确 - 完整的错误处理
const fetchData = async () => {
  loading.value = true
  try {
    const res = await userAPI()
    if (res.code === 1) {
      dataList.value = res.data
    } else {
      Taro.showToast({
        title: res.msg || '请求失败',
        icon: 'none'
      })
    }
  } catch (err) {
    console.error('请求失败:', err)
    Taro.showToast({
      title: '网络异常',
      icon: 'none'
    })
  } finally {
    loading.value = false
  }
}

// ❌ 错误 - 没有错误处理
const fetchData = async () => {
  const res = await userAPI()
  dataList.value = res.data
}

SessionID 管理

// ✅ 正确 - sessionid 由请求拦截器自动注入
// 详见 src/utils/request.js:75-78

// ❌ 错误 - 手动设置 sessionid
const sessionid = Taro.getStorageSync('sessionid')
const res = await userAPI({ headers: { cookie: sessionid } })

🧪 测试规范

单元测试

// ✅ 好的测试 - 清晰的描述和断言
describe('formatDate', () => {
  it('should format date to YYYY-MM-DD', () => {
    const result = formatDate('2026-02-05')
    expect(result).toBe('2026-02-05')
  })

  it('should handle invalid date', () => {
    const result = formatDate('invalid')
    expect(result).toBe('')
  })
})

测试覆盖率目标

  • 全局覆盖率: > 80%
  • 核心业务逻辑: > 90%
  • 工具函数: 100%

📝 注释规范

JSDoc 注释

/**
 * 格式化日期
 * @param {string|Date} date - 日期对象或日期字符串
 * @param {string} format - 格式化模板(默认:'YYYY-MM-DD')
 * @returns {string} 格式化后的日期字符串
 * @example
 * formatDate('2026-02-05', 'YYYY年MM月DD日')
 * // 返回:'2026年02月05日'
 */
const formatDate = (date, format = 'YYYY-MM-DD') => {
  // ...
}

复杂逻辑注释

// 步骤 1: 验证用户权限
const hasPermission = await checkUserPermission()
if (!hasPermission) {
  throw new Error('无权限操作')
}

// 步骤 2: 检查数据有效性
const isValid = validateData(data)
if (!isValid) {
  throw new Error('数据格式错误')
}

// 步骤 3: 提交到服务器
const result = await submitData(data)

🔒 安全规范

防止 XSS

<!-- ❌ 危险 - 用户输入未转义 -->
<view v-html="userComment"></view>

<!-- ✅ 安全 - 使用 text 插值 -->
<view>{{ userComment }}</view>

敏感信息保护

// ❌ 错误 - 硬编码密钥
const API_KEY = 'sk-proj-xxxxx'

// ✅ 正确 - 使用环境变量
const API_KEY = process.env.API_KEY

✅ 代码检查清单

提交代码前,确认:

  • 代码可读性强,命名清晰
  • 函数单一职责,长度 < 50 行
  • 无深度嵌套(> 4 层)
  • 无魔法数字,使用常量
  • 无 console.log 或 debugger
  • Props 有类型和默认值
  • Emits 有事件名定义
  • API 调用检查 res.code === 1
  • 所有 async 函数有 try-catch
  • 组件样式使用 scoped
  • 优先使用 TailwindCSS
  • 复杂逻辑有注释说明
  • JSDoc 注释完整

维护者: 开发团队 最后更新: 2026-02-05