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

865 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 代码编写规范
## 目录
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 项目