shop-wx/doc/代码编写规范.md

865 lines
16 KiB
Markdown
Raw Normal View History

# 代码编写规范
## 目录
1. [项目结构规范](#1-项目结构规范)
2. [TypeScript 规范](#2-typescript-规范)
3. [API 设计规范](#3-api-设计规范)
4. [页面开发规范](#4-页面开发规范)
5. [组件开发规范](#5-组件开发规范)
6. [状态管理规范](#6-状态管理规范)
7. [样式规范](#7-样式规范)
8. [工具函数规范](#8-工具函数规范)
9. [命名规范](#9-命名规范)
10. [注释规范](#10-注释规范)
---
## 1. 项目结构规范
### 1.1 目录结构
```
src/
├── api/ # API 接口层
│ ├── module/ # 按业务模块分组
│ │ ├── index.ts # 导出该模块 API
│ │ └── model/ # 类型定义
│ │ └── index.ts
│ └── index.ts # 统一导出
├── components/ # 组件
│ ├── business/ # 业务组件
│ ├── common/ # 通用组件
│ └── icons/ # 图标组件
├── pages/ # 页面
│ └── module/ # 按业务模块分组
│ ├── index.vue # 页面主文件
│ └── components/ # 页面专用组件
├── store/ # 状态管理Pinia
├── utils/ # 工具函数
├── hooks/ # 自定义 Hooks
├── types/ # 全局类型定义
├── http/ # HTTP 请求封装
├── router/ # 路由配置
├── layouts/ # 布局组件
├── tabbar/ # 底部导航
└── config/ # 配置文件
```
### 1.2 文件命名规范
- **页面文件**:使用 kebab-case`user-profile.vue`
- **组件文件**:使用 kebab-case`user-card.vue`
- **工具文件**:使用 camelCase`formatDate.ts`
- **类型文件**:使用 camelCase`userTypes.ts`
- **API 文件**:使用 camelCase`userApi.ts`
- **常量文件**:使用 camelCase`appConfig.ts`
---
## 2. TypeScript 规范
### 2.1 类型定义
- 使用 `interface` 定义对象类型
- 使用 `type` 定义联合类型、基础类型别名
- 接口命名使用 PascalCase
- 私有属性使用下划线前缀(可选)
```typescript
/**
* 用户信息
*/
export interface User {
/** 用户ID */
id: number;
/** 用户名 */
username: string;
/** 邮箱 */
email?: string;
}
/**
* 用户登录参数
*/
export interface LoginParams {
username: string;
password: string;
remember?: boolean;
}
```
### 2.2 泛型使用
- 为 API 返回类型添加泛型约束
- 为组件 Props 添加类型约束
```typescript
// API 返回类型
export interface ApiResult<T> {
code: number;
data: T;
message: string;
}
// 组件 Props 类型
interface Props {
userInfo: User;
loading?: boolean;
}
```
---
## 3. API 设计规范
### 3.1 文件组织
- 按业务模块创建目录
- API 函数统一在 `index.ts` 中导出
- 类型定义放在 `model/index.ts`
### 3.2 函数命名
- 使用 camelCase 命名
- 使用动词 + 名词的方式命名
- 常用动词:`get`、`post`、`put`、`delete`、`add`、`update`、`remove`
```typescript
// ✅ 正确的命名
export function getUserList(params: UserParams) {
return http.get<User[]>('/api/users', params);
}
export function createUser(data: CreateUserData) {
return http.post<User>('/api/users', data);
}
// ❌ 错误的命名
export function userList() {}
export function add_new_user() {}
```
### 3.3 返回处理
- 统一检查响应状态
- 成功时返回数据,失败时抛出错误
```typescript
export async function getUserInfo(id: number) {
const res = await http.get<ApiResult<User>>(`/api/users/${id}`);
if (res.data.code === 200) {
return res.data.data;
}
return Promise.reject(res.data.message);
}
```
### 3.4 参数说明
- 为每个 API 函数添加中文注释
- 说明参数含义和是否必填
```typescript
/**
* 获取用户列表
* @param params 查询参数
* @param params.keyword 搜索关键词(可选)
* @param params.page 当前页码(可选)
* @param params.size 每页数量(可选)
*/
export function getUserList(params?: UserParams) {
return http.get<ApiResult<PageResult<User[]>>>('/api/users', params);
}
```
---
## 4. 页面开发规范
### 4.1 基本结构
```vue
<script lang="ts" setup>
// 导入顺序:框架 -> 第三方库 -> 工具库 -> 本地模块
import { ref, computed, onMounted } from 'vue';
import { getUserList } from '@/api/user';
// 页面逻辑...
definePage({
style: {
navigationBarTitleText: '用户列表',
},
});
</script>
<template>
<!-- 模板内容 -->
</template>
<style scoped lang="scss">
/* 样式内容 */
</style>
```
### 4.2 变量声明
- 使用 `ref()` 声明基础类型响应式数据
- 使用 `reactive()` 声明对象类型响应式数据
- 使用 `computed()` 声明计算属性
- 变量命名使用 camelCase
```typescript
// 基础类型
const loading = ref(false);
const currentPage = ref(1);
// 对象类型
const searchParams = reactive({
keyword: '',
status: '',
});
// 计算属性
const userList = computed(() => store.users);
```
### 4.3 函数声明
- 使用箭头函数或 function 声明
- 函数命名使用 camelCase
- 添加必要的注释说明
```typescript
/**
* 搜索用户
*/
const handleSearch = () => {
loading.value = true;
getUserList(searchParams)
.then((res) => {
// 处理成功
})
.finally(() => {
loading.value = false;
});
};
/**
* 分页改变
* @param page 页码
*/
const handlePageChange = (page: number) => {
currentPage.value = page;
handleSearch();
};
```
### 4.4 生命周期钩子
- 使用 Composition API 的生命周期钩子
- 常用钩子:`onMounted`、`onUnmounted`、`onShow`、`onHide`
```typescript
onMounted(() => {
initData();
});
onShow(() => {
refreshData();
});
```
---
## 5. 组件开发规范
### 5.1 组件结构
```vue
<script lang="ts" setup>
import { ref } from 'vue';
// Props 定义
interface Props {
title: string;
size?: 'large' | 'medium' | 'small';
}
const props = withDefaults(defineProps<Props>(), {
size: 'medium',
});
// Emits 定义
const emit = defineEmits<{
(e: 'update:visible', visible: boolean): void;
(e: 'confirm', data: any): void;
}>();
// 组件逻辑...
</script>
<template>
<!-- 模板内容 -->
</template>
<style scoped lang="scss">
/* 样式内容 */
</style>
```
### 5.2 Props 规范
- 使用 TypeScript 类型约束
- 使用 `withDefaults` 提供默认值
- Props 命名使用 camelCase
```typescript
interface Props {
/** 标题文本 */
title: string;
/** 是否显示v-model */
modelValue: boolean;
/** 大小尺寸 */
size?: 'large' | 'medium' | 'small';
/** 是否禁用 */
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
size: 'medium',
disabled: false,
});
```
### 5.3 Emits 规范
- 使用 `defineEmits` 定义事件
- 事件名使用 kebab-case
- 提供完整的类型定义
```typescript
const emit = defineEmits<{
(e: 'update:visible', visible: boolean): void;
(e: 'confirm', data: any): void;
(e: 'cancel'): void;
}>();
const handleConfirm = () => {
emit('confirm', data);
};
```
### 5.4 组件命名
- 组件文件使用 PascalCase 或 kebab-case
- 推荐使用 PascalCase
```vue
<!-- ✅ 推荐 -->
UserCard.vue
UserInfoForm.vue
<!-- ❌ 不推荐 -->
user-card.vue
user_info_form.vue
```
---
## 6. 状态管理规范
### 6.1 Store 定义
- 使用 Pinia 的 setup 语法
- 使用 `defineStore` 创建 store
- state 使用 `ref` 声明
- actions 直接声明为函数
```typescript
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { getUserList } from '@/api/user';
export const useUserStore = defineStore('user', () => {
// 状态
const userList = ref([]);
const currentUser = ref(null);
const loading = ref(false);
// 异步操作
const fetchUserList = async () => {
loading.value = true;
try {
const data = await getUserList();
userList.value = data;
} finally {
loading.value = false;
}
};
// 同步操作
const setCurrentUser = (user: any) => {
currentUser.value = user;
};
return {
userList,
currentUser,
loading,
fetchUserList,
setCurrentUser,
};
});
```
### 6.2 Store 使用
- 在页面或组件中引入 store
- 使用 camelCase 命名 store 实例
```typescript
import { useUserStore } from '@/store/user';
const userStore = useUserStore();
// 使用 store
userStore.fetchUserList();
```
---
## 7. 样式规范
### 7.1 样式作用域
- 组件样式必须使用 `scoped` 属性
- 避免全局样式污染
```vue
<style scoped lang="scss">
.example {
color: #333;
}
</style>
```
### 7.2 命名规范
- 使用 BEM 命名规范
-Block使用 kebab-case
- 元素Element使用 `__` 连接
- 修饰符Modifier使用 `--` 连接
```scss
.user-card {
// 块
&__header {
// 元素
}
&__title {
// 元素
}
&--large {
// 修饰符
}
}
```
### 7.3 样式组织
- 按元素类型分组
- 相关样式放在一起
- 避免重复代码
```scss
.card {
padding: 16px;
border: 1px solid #e0e0e0;
border-radius: 8px;
// 标题样式
&__title {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
}
// 内容样式
&__content {
color: #666;
line-height: 1.6;
}
// 按钮样式
&__button {
margin-top: 16px;
text-align: right;
}
}
```
### 7.4 响应式设计
- 使用 rpx 单位适配不同屏幕
- 使用媒体查询或 uni-app 的响应式方案
```scss
.container {
padding: 20rpx;
@media (min-width: 768px) {
padding: 40rpx;
}
}
```
---
## 8. 工具函数规范
### 8.1 函数命名
- 使用 camelCase 命名
- 函数名应该清晰表达功能
```typescript
// ✅ 正确的命名
export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): T {
// 实现...
}
// ❌ 错误的命名
export function f(d: Date): string {}
export function deb() {}
```
### 8.2 函数注释
- 为复杂函数添加 JSDoc 注释
- 说明参数和返回值
```typescript
/**
* 防抖函数
* @param func 要防抖的函数
* @param delay 延迟时间(毫秒)
* @returns 防抖后的函数
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): T {
// 实现...
}
```
### 8.3 工具分类
- 通用工具放在 `src/utils/` 目录
- 按功能分类创建文件
- 导出时统一在 `index.ts` 中聚合
```typescript
// src/utils/date.ts
export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
// src/utils/index.ts
export * from './date';
export * from './string';
export * from './number';
```
---
## 9. 命名规范
### 9.1 文件命名
- 页面文件kebab-case
- 组件文件PascalCase 或 kebab-case
- 工具文件camelCase
- 类型文件camelCase
```
pages/
user-profile.vue
order-list.vue
components/
UserCard.vue
user-info-form.vue
utils/
formatDate.ts
validateForm.ts
types/
userTypes.ts
apiTypes.ts
```
### 9.2 变量命名
- 基础变量camelCase
- 常量:大写字母 + 下划线
- 私有变量:下划线前缀(可选)
```typescript
// 基础变量
const userName = '张三';
const userList = [];
// 常量
const API_BASE_URL = 'https://api.example.com';
const MAX_RETRY_COUNT = 3;
// 私有变量(可选)
const _privateVar = '私有变量';
```
### 9.3 函数命名
- 使用 camelCase
- 使用动词 + 名词的方式
```typescript
// 动词 + 名词
function getUserInfo() {}
function updateUserData() {}
function deleteUserById() {}
// 布尔值命名
const isLoading = ref(false);
const hasPermission = ref(true);
const canEdit = ref(false);
```
### 9.4 接口命名
- 使用 PascalCase
- 可以添加后缀说明类型
```typescript
interface User {}
interface UserInfo {}
interface UserParams {}
interface UserListResponse {}
```
---
## 10. 注释规范
### 10.1 文件注释
- 在文件顶部添加文件说明(可选)
```typescript
/**
* 用户管理相关 API
* 包含用户信息的增删改查功能
*/
import type { User } from './types';
```
### 10.2 函数注释
- 使用 JSDoc 格式
- 说明函数功能、参数和返回值
```typescript
/**
* 获取用户信息
* @param id 用户ID
* @param includeRoles 是否包含角色信息
* @returns 用户信息
*/
function getUserInfo(id: number, includeRoles = false): User {
// 实现...
}
```
### 10.3 类型注释
- 为接口属性添加注释
- 说明属性含义
```typescript
/**
* 用户信息
*/
interface User {
/** 用户ID */
id: number;
/** 用户名 */
username: string;
/** 邮箱地址 */
email?: string;
/** 创建时间 */
createTime: string;
}
```
### 10.4 复杂逻辑注释
- 为复杂业务逻辑添加注释
- 说明为什么这样做
```typescript
// 需要手动计算的原因是uni-app不提供原生的高度获取方法
const calculateHeight = () => {
// 实现...
};
// 由于后端接口返回的数据结构与前端期望不一致,需要进行转换
const transformData = (rawData: any) => {
// 实现...
};
```
---
## 11. 最佳实践
### 11.1 性能优化
- 使用 `computed` 缓存计算结果
- 使用 `v-show` 而非 `v-if` 频繁切换的组件
- 合理使用 `onMounted``watch`
### 11.2 代码复用
- 提取公共逻辑为自定义 Hook
- 提取公共组件
- 使用工具函数减少重复代码
### 11.3 错误处理
- 统一处理 API 错误
- 提供友好的错误提示
- 记录错误日志(可选)
```typescript
const fetchData = async () => {
try {
const data = await getData();
// 处理成功
} catch (error) {
uni.showToast({
title: '加载失败,请重试',
icon: 'none',
});
console.error('数据加载失败:', error);
}
};
```
### 11.4 代码审查
- 提交代码前进行自检
- 检查命名、注释、格式
- 确保代码可读性和可维护性
---
## 12. 常用代码片段
### 12.1 页面模板
```vue
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { getData } from '@/api/example';
definePage({
style: {
navigationBarTitleText: '页面标题',
},
});
const loading = ref(false);
const data = ref([]);
const init = async () => {
loading.value = true;
try {
data.value = await getData();
} finally {
loading.value = false;
}
};
onMounted(() => {
init();
});
</script>
<template>
<view class="page">
<!-- 内容 -->
</view>
</template>
<style scoped lang="scss">
.page {
padding: 20rpx;
}
</style>
```
### 12.2 组件模板
```vue
<script lang="ts" setup>
interface Props {
title: string;
modelValue: boolean;
}
const props = withDefaults(defineProps<Props>(), {
title: '',
modelValue: false,
});
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void;
(e: 'confirm'): void;
}>();
const handleClose = () => {
emit('update:modelValue', false);
};
</script>
<template>
<view class="dialog">
<!-- 内容 -->
</view>
</template>
<style scoped lang="scss">
.dialog {
/* 样式 */
}
</style>
```
### 12.3 Store 模板
```typescript
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { getData } from '@/api/example';
export const useExampleStore = defineStore('example', () => {
const data = ref([]);
const loading = ref(false);
const fetchData = async () => {
loading.value = true;
try {
data.value = await getData();
} finally {
loading.value = false;
}
};
return {
data,
loading,
fetchData,
};
});
```
---
## 总结
本规范旨在统一项目代码风格,提高代码质量和可维护性。开发者在编写代码时应当遵循以下原则:
1. **一致性**:遵循既定的命名规范和代码风格
2. **可读性**:使用清晰的命名和充分的注释
3. **可维护性**:保持代码简洁、模块化
4. **最佳实践**:遵循 Vue 3 和 TypeScript 的最佳实践
如果在开发过程中遇到特殊情况,可以与团队讨论后调整规范内容。
---
> **版本**v1.0.0
> **更新日期**2025-10-29
> **适用范围**uni-app + Vue 3 + TypeScript 项目