项目代码规范
一、概述
本文档定义了 web-admin 项目的代码规范,旨在提高代码质量、可读性和可维护性。所有团队成员都应该遵守这些规范。
适用范围:
- Vue 3 组件
- TypeScript 代码
- JavaScript 代码
- HTML/CSS/SCSS 代码
- 配置文件
相关文档:
二、命名规范
2.1 文件命名
规则:
- 使用 PascalCase(大驼峰)命名 Vue 组件
- 使用 kebab-case(短横线)命名其他文件
- 使用大写字母命名常量文件
示例:
# 正确
LoginForm.vue # Vue 组件 - PascalCase
user-list.vue # Vue 组件 - PascalCase
api.service.ts # 服务文件 - kebab-case
utils.ts # 工具文件 - kebab-case
CONSTANTS.ts # 常量文件 - 大写
# 错误
login_form.vue # ❌ 不使用下划线
Loginform.vue # ❌ 不使用连续大写
UserList.TS # ❌ 扩展名小写
2.2 变量命名
规则:
- 使用 camelCase(小驼峰)命名变量和函数
- 使用 PascalCase(大驼峰)命名类和接口
- 使用 UPPER_SNAKE_CASE(大写蛇形)命名常量
- 使用下划线
_开头命名私有变量
示例:
typescript
// 正确
const userName = "John Doe"; // 变量 - camelCase
function getUserInfo() {} // 函数 - camelCase
interface UserData {} // 接口 - PascalCase
class UserService {} // 类 - PascalCase
const MAX_COUNT = 100; // 常量 - UPPER_SNAKE_CASE
const _privateData = {}; // 私有变量 - 下划线开头
// 错误
const UserName = "John Doe"; // ❌ 变量不使用大驼峰
function GetUserInfo() {} // ❌ 函数不使用大驼峰
interface userData {} // ❌ 接口不使用小驼峰
const maxCount = 100; // ❌ 常量不使用小驼峰
2.3 组件命名
规则:
- 使用 PascalCase(大驼峰)命名组件
- 使用多个单词组合(至少两个单词)
- 避免使用 Vue、Component 等通用后缀
示例:
vue
<!-- 正确 -->
<template>
<LoginForm />
<UserList />
<ProductCard />
</template>
<!-- 错误 -->
<template>
<Login />
<User />
<Product />
<VueComponent />
</template>
2.4 路由命名
规则:
- 使用 kebab-case(短横线)命名路由路径
- 使用 camelCase(小驼峰)命名路由名称
示例:
typescript
// 正确
const routes = [
{ path: "/userList", name: "userList", component: UserList },
{ path: "/login-form", name: "loginForm", component: LoginForm },
];
// 错误
const routes = [
{ path: "/UserList", name: "UserList", component: UserList },
{ path: "/user_list", name: "user_list", component: UserList },
];
三、代码格式
3.1 缩进
规则:
- 使用 2 个空格缩进
- 不使用 Tab 缩进
示例:
typescript
// 正确
function getUserInfo() {
return {
name: "John",
age: 30,
};
}
// 错误
function getUserInfo() {
return {
name: "John",
age: 30,
};
}
3.2 引号
规则:
- 使用双引号
"而不是单引号' - 模板字符串使用反引号 `````
示例:
typescript
// 正确
const name = "John Doe";
const message = `Hello, ${name}`;
// 错误
const name = 'John Doe';
const message = 'Hello, ' + name;
3.3 分号
规则:
- 行尾必须加分号
示例:
typescript
// 正确
const name = "John Doe";
function getUserInfo() {
return name;
}
// 错误
const name = "John Doe"
function getUserInfo() {
return name
}
3.4 行宽
规则:
- 单行代码不超过 120 字符
- 超过时需要换行
示例:
typescript
// 正确
const result = calculateData(
param1,
param2,
param3,
param4,
);
// 错误
const result = calculateData(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
3.5 空格
规则:
- 运算符前后加空格
- 逗号后加空格
- 大括号前后加空格
- 函数参数括号后加空格
示例:
typescript
// 正确
const sum = a + b;
const list = [1, 2, 3];
const obj = { name: "John" };
function getUserInfo() {}
// 错误
const sum=a+b;
const list=[1,2,3];
const obj={name:"John"};
function getUserInfo(){
}
四、TypeScript 规范
4.1 类型定义
规则:
- 为所有变量、函数参数和返回值定义类型
- 避免使用
any类型 - 使用接口定义复杂类型
示例:
typescript
// 正确
interface User {
id: number;
name: string;
age?: number;
}
function getUserInfo(userId: number): User {
return { id: userId, name: "John" };
}
// 错误
function getUserInfo(userId) {
return { id: userId, name: "John" };
}
function getUserInfo(userId: number): any {
return { id: userId, name: "John" };
}
4.2 接口和类型
规则:
- 使用
interface定义对象类型 - 使用
type定义联合类型和交叉类型 - 接口名使用
I开头(可选)
示例:
typescript
// 正确
interface IUser {
id: number;
name: string;
}
type Status = "active" | "inactive" | "pending";
type UserWithStatus = IUser & { status: Status };
// 错误
type User = {
id: number;
name: string;
};
4.3 函数
规则:
- 为函数参数和返回值定义类型
- 使用箭头函数优先
- 避免使用
function关键字
示例:
typescript
// 正确
const getUserInfo = (userId: number): IUser => {
return { id: userId, name: "John" };
};
// 错误
function getUserInfo(userId) {
return { id: userId, name: "John" };
}
4.4 可选链和空值合并
规则:
- 使用可选链
?.访问嵌套属性 - 使用空值合并
??提供默认值
示例:
typescript
// 正确
const userName = user?.profile?.name ?? "Guest";
// 错误
const userName = user && user.profile && user.profile.name ? user.profile.name : "Guest";
const userName = user.profile.name || "Guest"; // ❌ 0 和 "" 会被覆盖
五、Vue 3 规范
5.1 组件结构
规则:
- 使用
<script setup>语法 - 按顺序组织代码:导入、状态、计算属性、方法、生命周期
- 模板使用 2 空格缩进
示例:
vue
<template>
<div class="user-profile">
<h2>{{ userName }}</h2>
<button @click="handleClick">Click</button>
</div>
</template>
<script setup lang="ts">
// 1. 导入
import { ref, computed, onMounted } from "vue";
import { useUserStore } from "@/store";
// 2. 状态
const userStore = useUserStore();
const userId = ref(1);
// 3. 计算属性
const userName = computed(() => userStore.userName);
// 4. 方法
const handleClick = () => {
console.log("Clicked");
};
// 5. 生命周期
onMounted(() => {
console.log("Mounted");
});
</script>
<style scoped lang="scss">
.user-profile {
padding: 16px;
}
</style>
5.2 Props 和 Emits
规则:
- 使用
defineProps定义 props - 使用
defineEmits定义 emits - 为 props 定义类型和默认值
示例:
vue
<script setup lang="ts">
interface Props {
userId: number;
userName?: string;
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
userName: "Guest",
isActive: false,
});
const emit = defineEmits<{
(e: "update", value: number): void;
(e: "delete", id: number): void;
}>();
const handleUpdate = () => {
emit("update", props.userId);
};
</script>
5.3 模板语法
规则:
- 使用双大括号
{``{ }}绑定数据 - 使用
v-bind或:绑定属性 - 使用
v-on或@绑定事件 - 使用
v-if而不是v-show用于条件渲染
示例:
vue
<template>
<!-- 正确 -->
<div :class="{ active: isActive }" :id="elementId">
<p>{{ userName }}</p>
<button @click="handleClick">Click</button>
<div v-if="isVisible">Content</div>
</div>
<!-- 错误 -->
<div class="active" id="elementId">
<p v-text="userName"></p>
<button v-on:click="handleClick">Click</button>
<div v-show="isVisible">Content</div>
</div>
</template>
5.4 样式
规则:
- 使用
scoped属性隔离样式 - 使用 SCSS 语法
- 使用 kebab-case 命名 CSS 类
- 使用 BEM 命名规范(可选)
示例:
vue
<style scoped lang="scss">
/* 正确 */
.user-profile {
padding: 16px;
&__header {
font-size: 18px;
}
&__content {
margin-top: 8px;
}
}
/* 错误 */
.UserProfile {
padding: 16px;
}
.userProfileHeader {
font-size: 18px;
}
</style>
六、代码质量
6.1 注释
规则:
- 使用 JSDoc 格式注释函数和类
- 为复杂逻辑添加注释
- 避免注释明显的代码
- 使用中文注释
示例:
typescript
/**
* 获取用户信息
* @param userId - 用户 ID
* @returns 用户信息对象
*/
const getUserInfo = async (userId: number): Promise<IUser> => {
// 调用 API 获取用户数据
const response = await api.get(`/users/${userId}`);
return response.data;
};
// 计算用户年龄(从出生日期计算)
const userAge = computed(() => {
const birthDate = new Date(userStore.birthDate);
const today = new Date();
return today.getFullYear() - birthDate.getFullYear();
});
6.2 错误处理
规则:
- 使用
try-catch处理异步错误 - 为所有 Promise 添加错误处理
- 提供友好的错误提示
示例:
typescript
// 正确
const fetchData = async () => {
try {
const response = await api.get("/data");
return response.data;
} catch (error) {
console.error("获取数据失败:", error);
ElMessage.error("获取数据失败,请稍后重试");
throw error;
}
};
// 错误
const fetchData = async () => {
const response = await api.get("/data");
return response.data;
};
6.3 性能优化
规则:
- 使用
computed缓存计算结果 - 使用
v-memo缓存模板 - 避免在模板中使用复杂表达式
- 避免在循环中使用
v-if
示例:
vue
<template>
<!-- 正确 -->
<div v-for="item in filteredList" :key="item.id">
{{ item.name }}
</div>
<!-- 错误 -->
<div v-for="item in list" :key="item.id" v-if="item.isActive">
{{ item.name }}
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
const list = ref([{ id: 1, name: "John", isActive: true }]);
// 正确:使用 computed 过滤列表
const filteredList = computed(() => list.value.filter((item) => item.isActive));
</script>
七、API 调用规范
7.1 API 封装
规则:
- 使用 Axios 封装 API 调用
- 为每个模块创建独立的 API 文件
- 使用拦截器处理请求和响应
示例:
typescript
// api/user.api.ts
import request from "@/utils/request";
export const userApi = {
getUserInfo: (userId: number) => request.get(`/users/${userId}`),
updateUserInfo: (data: IUser) => request.put("/users", data),
deleteUser: (userId: number) => request.delete(`/users/${userId}`),
};
7.2 请求和响应
规则:
- 使用统一的请求格式
- 使用统一的响应格式
- 处理分页和排序参数
示例:
typescript
// 请求参数
interface PageParams {
page: number;
pageSize: number;
sortBy?: string;
sortOrder?: "asc" | "desc";
}
// 响应格式
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
interface PageResponse<T> {
list: T[];
total: number;
page: number;
pageSize: number;
}
八、状态管理规范
8.1 Pinia Store
规则:
- 使用 Pinia 进行状态管理
- 为每个模块创建独立的 Store
- 使用
defineStore定义 Store
示例:
typescript
// store/modules/user.store.ts
import { defineStore } from "pinia";
interface UserState {
userId: number;
userName: string;
isLoggedIn: boolean;
}
export const useUserStore = defineStore({
id: "user",
state: (): UserState => ({
userId: 0,
userName: "",
isLoggedIn: false,
}),
getters: {
fullName: (state) => state.userName,
},
actions: {
setUserInfo(userInfo: UserState) {
this.userId = userInfo.userId;
this.userName = userInfo.userName;
this.isLoggedIn = true;
},
logout() {
this.userId = 0;
this.userName = "";
this.isLoggedIn = false;
},
},
});
九、代码审查规范
9.1 审查要点
-
代码质量
- 是否符合代码规范
- 是否有语法错误
- 是否有逻辑错误
-
性能优化
- 是否有性能问题
- 是否有内存泄漏
- 是否有重复请求
-
安全性
- 是否有 XSS 漏洞
- 是否有 CSRF 漏洞
- 是否有敏感信息泄露
-
可维护性
- 是否有足够的注释
- 是否有测试用例
- 是否有文档
9.2 审查流程
1. 提交代码到 Git
2. 创建 Pull Request
3. 分配审查人员
4. 审查代码
5. 修复问题
6. 合并代码
十、总结
10.1 关键规范
-
命名规范
- 组件使用 PascalCase
- 变量使用 camelCase
- 常量使用 UPPER_SNAKE_CASE
-
代码格式
- 2 空格缩进
- 双引号
- 行尾分号
- 120 字符行宽
-
TypeScript
- 定义所有类型
- 尽量避免使用 any
- 使用接口定义复杂类型
-
Vue 3
- 使用
<script setup> - 定义 Props 和 Emits
- 使用 scoped 样式
- 使用
10.2 工具支持
- ESLint - 代码检查
- Prettier - 代码格式化
- Husky - Git 钩子
- lint-staged - 暂存文件检查
附录:
版本历史:
- v1.0 (2026-01-16) - 初始版本
上一篇: 02-遇到的问题及解决方案.md
返回目录: README.md