编码规范
命名
方法
| 类型 | 规范 | 示例 |
|---|---|---|
| 事件处理 | handle + 动词 + 名词 | handleLoginClick、handleFormSubmit |
| 数据加载 | load/fetch + 名词 | loadUserInfo、fetchOrderList |
| 布尔判断 | is/has/should + 形容词/名词 | isLoading、hasPermission |
| 状态切换 | toggle + 名词 | toggleTheme、toggleMenu |
| 数据验证 | validate + 名词 | validateForm、validatePhone |
| 数据格式化 | format + 名词 | formatDate、formatBytes |
typescript
// ✅
const handleLoginClick = () => { /* ... */ }
const loadUserInfo = async () => { /* ... */ }
const isLoading = ref(false)
// ❌
const click = () => { /* ... */ }
const getUserInfo = async () => { /* ... */ } // 应该用 load/fetch
const loading = ref(false) // 应该用 isLoading变量
typescript
// 布尔值:is/has/should 前缀
const isLoading = ref(false)
const hasPermission = computed(() => !!getAccessToken())
// 数组:复数形式
const userList = ref([])
const menuItems = ref([])
// 对象:单数形式
const userInfo = ref({})
const formData = ref({})文件
| 类型 | 规范 | 示例 |
|---|---|---|
| 页面 | kebab-case | user-profile.vue |
| 组件 | PascalCase | UserCard.vue |
| Composables | camelCase + use 前缀 | useCountdown.ts |
| 工具函数 | camelCase | permission.ts |
| 类型定义 | PascalCase | User.ts |
Composables
满足任一条件时提取:
- 逻辑复用 — 多个组件使用相同逻辑
- 状态管理 — 复杂的响应式状态
- 副作用封装 — 定时器、事件监听等
typescript
// composables/useCountdown.ts
export function useCountdown(duration = 60) {
const countdown = ref(0)
const isRunning = ref(false)
let timer: ReturnType<typeof setInterval> | null = null
const start = () => {
if (isRunning.value) return
countdown.value = duration
isRunning.value = true
timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) stop()
}, 1000)
}
const stop = () => {
if (timer) { clearInterval(timer); timer = null }
countdown.value = 0
isRunning.value = false
}
onUnmounted(() => stop())
return { countdown, isRunning, start, stop }
}工具函数
utils/
├── format.ts # 格式化(日期、金额、文件大小)
├── permission.ts # 权限判断
├── auth.ts # 认证相关
├── request.ts # 网络请求
└── storage.ts # 本地存储原则:纯函数优先、避免依赖 Vue 实例、单一职责。
错误处理
typescript
// 统一在 request.ts 处理,业务层只需 try/catch 特殊场景
async function loadUserData() {
try {
userData.value = await UserAPI.getUserInfo()
} catch {
// 特殊业务处理(如提示用户重试)
}
}
// ❌ 不要到处写网络重试逻辑
const loadData = async (retryCount = 3) => { ... }跨平台兼容
typescript
// 条件编译
// #ifdef MP-WEIXIN
uni.setStorageSync(key, value)
// #endif
// #ifdef H5
window.localStorage.setItem(key, value)
// #endif安全区域:
scss
.tabbar {
padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* iOS >= 11.2 */
}触摸目标:可点击元素最小 88rpx × 88rpx(44px)。
反模式
| 反模式 | 正确做法 |
|---|---|
onMounted 空函数 | 直接删除 |
computed(() => someRef.value) 纯透传 | 直接用 someRef |
页面内定义 formatBytes 等通用函数 | 提取到 utils/ |
| 相同逻辑在多个页面重复(倒计时、导航点击) | 提取为 composable |
type X = Y 无约束别名 | 直接使用原类型 |
接口字段完全重复(Form 与 Item 相同) | 合并为一个或 type Item = Form |
checkLogin 函数 40+ 行 | 提取 getCurrentPagePath,简化主函数 |
自查清单
- [ ] 方法命名符合规范(handle/load/fetch 前缀)
- [ ] 布尔值 is/has/should 前缀
- [ ] 重复逻辑已提取为 composable
- [ ] 通用函数已提取到 utils
- [ ] 重复接口已合并
- [ ] 无空函数、死代码
