Appearance
前端开发规范
引言
本规范旨在为基于 Vue3、Vite 和 TypeScript 的前端项目开发提供统一的标准和指导,确保项目的代码质量、可维护性和团队协作效率。
Javascript规范
文件命名
- 全部采用 kebab-case 命名,字母小写,以短横分隔单词。render-dom.ts / home.ts / get-user-info.ts
声明变量
- 变量名使用 camelCase 驼峰命名。
- 不能以数字,特殊符号开头。
- 不能使用 JavaScript 关键字和保留字。
- 避免使用没有意义的命名。
- 变量名起名以 类型+对象描述 的规则。
typescript
let userId = 1;
let userName = "小明";
let tableTitle = "表格标题";声明常量
- 常量名使用全部大写命名,多个单词中间用_下划线分隔。
typescript
const AMOUNT = 10;
const MAX_COUNT = 10;
const APPROVE_TYPE = { DOING: 1, APPROVED: 2 };形参命名
| 参数 | 描述 | 示例 |
|---|---|---|
| i | 索引 | switchTab(i) {} |
| i,v | 值 | list.forEach((i,v) => {}) |
| e | 事件 | onInput(e) {} |
| obj | 对象 | for(let obj of userList) {} |
| arr | 数组 | let arr = []; |
| fun | 函数 | callback(fun) {} |
| item | 列表项 | list.map(item => {}) |
| item,index | vue遍历数据 | v-for="(item,index) in arr" |
| key,value | 键值对 | for(let [key,value] of arr) {} |
函数命名
- 函数名使用camelCase驼峰命名,命名为动词形式,或者动词+名词形式。
typescript
function getName() {}
function setName() {}
function canEdit() {}
function addUser() {}
function hasCode() {}- 增删改查和查看详情,统一使用下面的单词。
add / delete / update / get / detail
- 附带常用的动词列表
html
get 获取 | set 设置
add 增加 | delete 删除
start 启动 | launch 启动 | run 运行 | ready 准备 | stop 停止
open 打开 | close 关闭
read 读取 | write 写入 | save 保存
load 载入 | unload 不加载
create 创建 | destroy 销毁
begin 开始 | start 开始 | end 结束
from 从.. | to 到..
backup 备份 | restore 恢复
import 导入 | export 导出
print 打印 | afterprint 打印机关闭
split 分割 | merge 合并
touch 轻按 | click 点击 | dblclick 双击 | drag 拖拽
inject 注入 | extract 提取
attach 附着 | detach 脱离
bind 绑定 | separate 分离
view 查看 | browse 浏览
edit 编辑 | modify 修改 | update 更新 | upgrade 升级
select 选取 | change 改变 | mark 标记
copy 复制 | paste 粘贴
do 做 | undo 撤销 | redo 重做
insert 插入 | remove 移除 | revoke 撤销 | revert 复原 | reset 重置
clean 清理 | clear 清除
index 索引 | sort 排序
find 查找 | search 搜索
increase 增加 | decrease 减少
rise 上涨 | fall 下降
play 播放 | pause 暂停
forward 前进 | rewind 后退
compile 编译 | execute 执行
suspend 挂起 | complete 完成
debug 调试 | trace 跟踪
observe 观察 | listen 监听
build 构建 | publish 发布
input 输入 | output 输出
encode 编码 | decode 解码
encrypt 加密 | decrypt 解密
compress 压缩 | decompress 解压缩
pack 打包 | unpack 解包
parse 解析 | emit 生成
connect 连接 | disconnect 断开
send 发送 | receive 接收
upload 上传 | download 下载
refresh 刷新 | synchronize 同步
lock 锁定 | unlock 解锁
check out 签出 | check in 签入
submit 提交 | commit 交付
push 推 | pull 拉
expand 展开 | collapse 折叠
active 激活 | deactive 未激活
wait 等待 | process 进程中 | finish 完成
enter 进入 | exit 退出
abort 中止 | quit 离开
obsolete 废弃 | depreciate 废旧
collect 收集 | aggregate 聚集代码规范
三元条件简单判断
- 在简单的判断条件下,使用三元运算符能更加的精简代码,增加可读性。
typescript
let status = 0;
console.log(status == 0 ? "代办" : status == 1 ? "已办" : "无");创建对象
- 使用字面量创建对象,更加简洁
typescript
let user = {
id: 1,
name: "小红",
sex: "女",
age: 18,
};判断函数参数太多,封装为对象传参
typescript
let user = {
id: 1,
name: "小红",
sex: "女",
age: 18,
};
function addUser(user) {}使用 Promise,async/await 代替回调函数
typescript
// 使用Promise
function getMsg(res) {
return new Promise((resolve, reject) => {
if (res.code == 200) {
resolve(res);
} else {
reject(res);
}
});
}
getMsg({ code: 200, result: [] }).then((res) => {});
// 使用ssync/await
async function login() {
// 先执行获取用户信息
let user = await getUserInfo();
// 再执行获取菜单,传入用户的id
let menu = await getMenuList(user.id);
console.log(user, menu);
}TypeScript规范
命名规范
变量和函数命名
typescript
// 变量
let userName: string = 'John';
// 函数
function getFullName(firstName: string, lastName: string): string {
return `${firstName} ${lastName}`;
}类和接口命名
- 大驼峰命名法:类和接口名使用大驼峰命名法,即每个单词的首字母都大写。
typescript
// 类
class UserProfile {
(public name: string, public age: number) {}
}
// 接口
interface Person {
name: string;
age: number;
}常量命名
- 全大写单词间用下划线分隔:常量使用全大写字母,单词之间用下划线分
typescript
const MAX_COUNT: number = 100;类型定义规范
明确类型注释
- 尽可能明确地指定变量、函数参数和返回值的类型,避免使用隐式类型或 any 类型,除非确实无法确定类型。
typescript
// 明确指定类型
let score: number = 90;
// 函数参数和返回值类型注解
function add(a: number, b: number): number {
return a + b;
}类型别名和接口使用
- 类型别名:适用于简单类型的重命名、联合类型、交叉类型等。
typescript
// 类型别名
type ID = string | number;
type User = {
id: ID;
name: string;
};- 接口:更适合用于定义对象的结构,可实现继承和扩展。
typescript
// 接口
interface Animal {
name: string;
eat(): void;
}
interface Dog extends Animal {
bark(): void;
}- 枚举类型的使用
枚举类型使用大驼峰命名法,枚举成员通常使用全大写,单词间用下划线分隔。
typescript
enum Color {
RED = 'red',
GREEN = 'green',
BLUE = 'blue'
}代码结构规范
模块化开发
- 使用 ES6 模块语法(import 和 export)来组织代码,每个文件应具有单一的职责。
typescript
// utils.ts
export function formatDate(date: Date): string {
return date.toLocaleDateString();
}
// main.ts
import { formatDate } from './utils';
const now = new Date();
const formattedDate = formatDate(now);类的结构
- 类的成员按照以下顺序排列:静态属性、实例属性、构造函数、静态方法、实例方法。
typescript
class Calculator {
static PI: number = 3.14;
private result: number = 0;
(initialValue: number) {
this.result = initialValue;
}
static add(a: number, b: number): number {
return a + b;
}
addToResult(value: number): void {
this.result += value;
}
getResult(): number {
return this.result;
}
}命名规则
布尔值命名
- 布尔值是两种逻辑状态的变量真和假,对应 true 和 false ,也可以用数字 1 和 0 表示真假。
- 推荐命名方式为 is + 动词(现在进行时)/ 形容词 ,但是有些场景下也可以不用写 is 开头。
可见性/进行中
json
{
"isShow": "是否显示",
"isVisible": "是否可见",
"isLoading": "是否处于加载中",
"isConnecting": "是否处于连接中",
"isValidating": "正在验证中",
"isDoing": "正在进行中",
"isRunning": "正在运行中",
"isListening": "正在监听中"
}属性/状态
json
{
"isDisabled": "是否禁用",
"isEdit": "是否可编辑",
"isAdd": "是否可增加",
"isUpdate": "是否可修改",
"isRemove": "是否可删除",
"isClearable": "是否可清空",
"isReadonly": "是否只读",
"isExpand": "是否可展开",
"isShrink": "是否可收缩",
"isChecked": "是否选中",
"isClick": "是否可点击",
"isDrag": "是否可拖拽",
"isOpen": "是否打开",
"isClose": "是否打开",
"isChoice": "是否选择(复选框,单选框)",
"isSelect": "是否选择(下拉框,列表)",
"isConfig": "是否可配置"
}弹框显示/隐藏
json
{
// 组合1-基础表单
"addModal": "新增弹框",
"editModal": "编辑弹框",
"detailModal": "详情弹框",
// 组合2-业务相关
"syncDataModal": "同步数据弹框",
"addUserModal": "新增用户弹框",
"userInfoModal": "用户信息弹框",
"assignPermissionModal": "分配权限弹框",
"departTreeModal": "部门树弹框"
}数组变量命名
- 数组主要有 复数形式 和 xxxList 形式,简单的区别是 复数形式 的适合前端本身声明的数组, xxxList 形式的适合后台接口返回的数组。
typescript
{
// 前端声明变量
"users": "用户列表",
"menus": "菜单列表",
"selectNodes": "选中节点列表",
// 后台返回数据接收变量
"userList": "用户列表",
"menuList": "菜单列表",
"statusList": "状态列表"
}变量命名选项变量命名
- 主要针对的是单选框,复选框,下拉框,选项卡等元素数据。
- 常见的词汇有: title, name,label,field,text,id,key,value,children,index,nodes 等。
- 其中 title,name,label,text,field 作为选项显示名, id,key,value 用于唯一标识, children,nodes 用于包含子节点内容。
复选/单选/下拉
json
{
// 常用组合
"label": "标题",
"value": "值",
// 组合1
"title": "标题",
"value": "值",
// 组合2
"text": "文本",
"value": "值",
// 组合3
"id": "标题",
"name": "值",
// 组合4
"field": "标题",
"value": "值",
"index": "标题"
}激活状态
json
{
"activeName": "激活的项名称",
"activeTab": "激活的选项卡",
"currentPage": "当前页",
"currentIndex": "当前选项的下标"
}Api接口命名
- 主要用作 axios 请求后台接口封装,接口方法使用camelCase驼峰命名,命名为动词+名词形式。
- 后台一般采用 Restful Api 规范,如下示例:
text
/api/user/list
/api/user/detail/${id}
/api/user/add
/api/user/update
/api/user/delete/${id}- 前端对应的接口方法命名用getXxx,addXxx,updateXxx,deleteXxx这几种命名形式,并且一定要写注释,注释格式为模块名-方法描述,如下示例:
typescript
/**
* 用户管理-分页列表
*/
export const getUserList = (params) => {
return axios.get("/api/user/list", params);
};
/**
* 用户管理-详情
*/
export const getUserDetail = (params) => {
return axios.get(`/api/user/detail/${params.id}`);
};
/**
* 用户管理-新增
*/
export const addUser = (data) => {
return axios.post("/api/user/add", data);
};
/**
* 用户管理-修改
*/
export const updateUser = (params) => {
return axios.post("/api/user/update", data);
};
/**
* 用户管理-删除
*/
export const deleteUser = (params) => {
return axios.get(`/api/user/delete/${params.id}`);
};命名参考
样式文件命名
text
index.css // 一般用于首页建立样式
head.css // 头部样式,当多个页面头部设计风格相同时使用。
base.css // 共用样式。
style.css // 独立页面所使用的样式文件。
global.css // 页面样式基础,全局公用样式,页面中必须包含。
layout.css // 布局、版面样式,公用类型较多时使用,一般用在首页级页面和产品类页面中
module.css // 模块,用于产品类页,也可与其它样式配合使用。
master.css // 主要的样式表
columns.css // 专栏样式
themes.css // 主体样式
forms.css // 表单样式
mend.css // 补丁,基于以上样式进行的私有化修补。
print.css // 打印页面结构命名
text
page // 代表整个页面,用于最外层。
wrap // 外套,将所有元素包在一起的一个外围包,用于最外层
wrapper // 页面外围控制整体布局宽度,用于最外层
container // 一个整体容器,用于最外层
head|header // 页头区域,用于头部
nav // 导航条
content // 内容,网站中最重要的内容区域,用于网页中部主体
main // 网站中的主要区域(表示最重要的一块位置),用于中部主体内容
column // 栏目
sidebar // 侧栏
foot|footer // 页尾、页脚。网站一些附加信息放置区域,(或命名为 copyright)用于底部
left|right|center // 左右中导航命名
text
nav|navbar|navigation|nav-wrapper // 导航条或导航包,代表横向导航
topnav // 顶部导航
mainbav // 主导航
subnav // 子导航
sidebar // 边导航
left-sidebar|sidebar-l // 左导航
right-sidebar|sidebar-r // 右导航
title // 标题
summary // 摘要
menu // 菜单,区域包含一般的链接和菜单
submenu // 子菜单
drop // 下拉
dorp-menu // 下拉菜单
links // 链接菜单功能命名
text
logo // 标记网站logo标志
banner // 标语、广告条、顶部广告条
login // 登陆,(例如: 登录表单 form-login)
loginbar // 登录条
register // 注册
tool|toolbar // 工具条
search // 搜索
searchbar // 搜索条
searchlnput // 搜索输入框
shop // 功能区,表示现在的
icon // 小图标
label // 商标
homepage // 首页
subpage // 二级页面子页面
hot // 热门热点
list // 文章列表,(例如: 新闻列表 list-news)
scroll // 滚动
tab // 标签
sitemap // 网站地图
msg|message // 提示信息
current // 当前的
joinus // 加入
status // 状态
btn // 按钮,(例如: 搜索按钮可写成 btn-search)
tips // 小技巧
note // 注释
guild // 指南
arr|arrow // 标记箭头
service // 服务
breadcrumb // (即页面所处位置导航提示)
download // 下载
vote // 投票
news // 新闻
siteinfo // 网站信息
partner // 合作伙伴
link|friendlink // 友情链接
copyright // 版权信息
siteinfoCredits // 信誉
siteinfoLegal // 法律信息注释规范
文件注释
- 文件开头添加注释,描述功能、作者、创建日期等,如:
typescript
/**
* @fileoverview 该文件定义用户相关 API 服务函数
* @author John Doe <john@example.com>
* @date 2025 - 01 - 01
*/函数注释
- 复杂函数定义上方添加 JSDoc 风格注释,描述功能、参数、返回值等,如:
typescript
/**
* 计算两个数的和
* @param {number} num1 - 第一个数
* @param {number} num2 - 第二个数
* @returns {number} 两个数的和
*/
/**
* @description@description 描述说明
* @param param {参数类型} 参数说明
* @returns returns {返回类型} 返回结果说明
* @author author 作者
* @version version 版本号
* @example example 示例代码
* @todo todo 待办的内容描述
*/
function addNumbers(num1: number, num2: number): number {
return num1 + num2;
}组件注释
- Vue 组件 script 标签上方添加注释,描述功能、props、emits 等,如:
typescript
<script lang="ts">
/**
* @description 用户登录组件,用于用户输入用户名和密码登录
* @props {string} username - 用户名,默认值为空字符串
* @props {string} password - 密码,默认值为空字符串
* @emits {event} login - 用户点击登录按钮且验证通过时触发,传递登录成功用户信息
*/
</script>Vue开发规范
组件命名
- 在views目录下单文件统一采用kebab-case命名,字母小写,以短横分隔单词。
- 组件名不能和 html 标签元素冲突。
- 组件名应该以描述性单词开头,以 修饰性单词 结尾。
- 使用有意义的单词组合, 2~3 个 单词最佳,单词不能过长。
- 只有一个单词的组件全部都要小写。
text
login.vue
home.vue
user-info.vue
base-config.vue
search-button-clear.vue紧密耦合的组件名
- 如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上,因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。
text
├─ todo-list.vue
├─ todo-list-item.vue
└─ todo-list-item-button.vue公用组件
局部公用
- 在当前功能的目录新建 components 文件夹,所有声明的组件放在里面,采用 PascalCase 命名,并且功能目录需要有 index.vue 入口文件。
text
src/views/user-info
├─ index.vue
└─ components/
├─ BaseInfo.vue
└─ UserInfo.vue全局公用
- 统一放到工程目录的 src/components 下面,目录名称采用 PascalCase 命名,并且每个公用组件目录必须有 index.vue 入口文件,Attribute属性和 Event 事件的命名都是要用 kebab-case 命名,不能用驼峰命名。
text
src/components/
├─ PageHeader
└─ index.vue
├─ UploadData
└─ index.vuetemplate模版中使用
- 需要用 kebab-case 命名
vue
<template>
<div class="app-container">
<user-info :user-detail="userDetail"></user-info>
</div>
</template>组件结构
- 遵循单一职责原则,一个组件负责一个特定功能。
- template 简洁,复杂逻辑在 script 中用计算属性或方法处理。
- 使用 setup 函数编写组件逻辑,通过 defineProps 和 defineEmits 定义属性和事件。全局样式在 src/styles 目录创建文件并在 main.ts 引入。
组件通信
- 父子组件通信:父传子通过 props ,子组件用 defineProps 定义并指定类型和默认值。
- 子传父:通过 $emit 触发事件,子组件用 defineEmits 定义,父组件用 v-on 监听。
- 兄弟组件通信:可通过 中央事件总线 或 pinia 实现。
- 跨层级组件通信:推荐 pinia ,简单情况可使用 provide 和 inject 组合。
模板中引入资源和组件的顺序
- 在模板中引入资源是有顺序的,一般是 先公用后局部 ,资源引入顺序 为js资源 > 组件 > css资源 ,如果引入资源比较多,需要合理的分组并写上注释。
vue
// 引入第三方工具类
import util from "@lime-util/util";
// 引入第三方组件
import VueScroll from "vuescroll";
// 引入公用方法
import { uploadFile } from "@/common";
// 引入公用组件
import PageHeader from "@/components/PageHeader";
// 引入api
import {
getUserList,
getUserDetail,
addUser,
updateUser,
deleteUser,
} from "@/api/user";
// 引入局部组件
import UserInfo from "./components/UserInfo";组件name大写
- 声明组件的 name 使用 PascalCase 帕斯卡命名,尽量每个组件页面都写,方便 vue 识别,或者在使用 keep-alive 时候做缓存。
vue
defineOptions({
name: "UpdateForm",
})传参数属性命名
- 主要针对 Vue 组件中 attribute 传参,采用 kebab-case 命名,字母小写,以短横分隔单词。
普通attribute
- 属性类型是 String,Number,Boolean,Object,Array,Date 这几种数据类型,采用 名词 或者 名词+修饰词 形式。
json
{
"class-name": "类名",
"z-index": "层级",
"fullscreen": "全屏显示",
"show-upload-list": "是否展示上传文件列表"
}function类型
- 场景:传参 function 组件除了能传普通的数据类型,还能传递 function ,采用 介词+动词/形容词 形式,注意这里事件名是允许用 on-xx 形式的,用来区分该 props 传递的是个函数。
json
{
"on-success": "上传成功",
"on-progress": "上传进度",
"on-error": "上传失败",
"on-preview": "预览",
"before-upload": "上传之前",
"before-close": "关闭之前",
"after-close": "关闭之后"
}组件prop定义
- 必须使用 camelCase 驼峰命名。
- 必须指定参数类型。
- 必须加上注释,表明参数的含义。
- 必须加上 default 默认值。
- 推荐加上 required ,如果有业务校验也需要加上 validator 。
typescript
// 简洁的
const props = defineProps({
userDetail: Object,
})
// 符合eslint推荐的
const props = defineProps({
userDetail: {
type: Object,
default: () => {}
},
})
// 更加详情,带业务校验的
const props = defineProps({
userDetail: {
type: Object,
required: true,
validator: function (value) {
// 校验
}
}
})函数命名
绑定事件监听
- 绑定的事件名采用 kebab-case 命名,字母小写,以短横分隔单词。因为在 Dom 模板 中的事件监听,由于 HTML 中是不区分大小写的,Vue 会自动转换事件监听为全小写,所以推荐 始终使用 kebab-case 的命名。
- 事件的命名采用动词形式,事件绑定本身就是 v-on 指令,不能写成 v-on:on-close ,所以绑定的事件统一都不用 on-xx 的形式。
json
{
// 原生事件监听
"input": "输入",
"change": "改变",
"blur": "失去焦点",
"focus": "聚焦",
"submit": "提交",
"keyup": "提交",
"keydown": "提交",
"enter": "提交",
"submit": "提交",
// 自定义事件监听
"preview": "预览",
"clear": "清空",
"cancel": "关闭",
"open": "打开",
"select-change": "选择",
"select-all": "选择所有",
"selection-change": "选项改变",
"sort-change": "排序",
"row-click": "单击行",
"row-dblclick": "双击行"
}- 在声明 事件监听函数 或者声明 props 绑定函数 的时候有些是需要用 onXxx 的形式,有些则可以不带 on,具体要看当前环境中的作用和语义。
typescript
// 组合1
function onInput() {}
function onPreview() {}
function onSelectAll() {}
function onRowClick() {}
function onSelectionChange() {}
// 组合2
function beforeUpload() {}
function beforeOpen() {}
function beforeClose() {}自定义事件
- 自定义事件名使用 camelCase 驼峰命名,命名为 动词+名词 形式。
- 名称不能过长,要明确表达此事件处理的作用,避免望文不知义。
- 可以参考 JavaScript 规范中 函数命名 章节,使用常用的动词来命名。
- 一般由页面直接或者间接发起的操作,用 handleXxx 的形式。
typescript
function handleSearch() {}
function handleReset() {}
function handleResize() {}- 由方法发起调用的操作,用 动词 开头的形式,命名看方法的用途决定
typescript
function resetQuery() {}
function resetForm() {}
function resetSelect() {}- 在其他场景下,很多命名方式也都可以用 动词+形容词/名词 形式,例如 elect+范围,元素+click,change+元素,clear+元素 ,这些动词开头,用在改变元素,改变分页,改变业务状态,切换 Modal 弹框显示等等。
typescript
// 组合1-元素操作
function selectOne() {}
function selectAll() {}
function tabClick() {}
function switchTab() {}
function clearName() {}
// 组合2-分页
function changePageNum() {}
function changePageSize() {}
function jumpPage() {}
// 组合3-业务弹框
function showAddModal() {}
function showEditModal() {}
function showDetailModal() {}
function showSyncDataModal() {}
function showUserInfoModal() {}
function showDepartTreeModal() {}
// 组合4-业务状态
function changeUserStatus() {}
function changeUserStatus() {}
function changeUserPermission() {}异步处理方法
- 在了解过接口命名规则之后,前端页面中使用 Api 接口 的异步事件,这些一般比较固定,以下组合列举出了一些场景。
typescript
// 组合1
// 查询的是后台返回的列表,推荐是用 queryXxxList 这种形式
function queryList() {}
function queryMenuList() {}
function queryUserList() {}
function queryDetail() {}
// 组合2
// 查询的是后台返回的数据信息,详情等,推荐用 queryXxx+Data/Info/Detail 这种形式
function queryProcessData() {}
function queryStepInfo() {}
function queryTaskDetail() {}
// 组合3
// 页面增删查改的操作,用 handleXxx 的形式
function handleAdd() {}
function handleEdit() {}
function handleDelete() {}
function handleSubmit() {}Git 提交规范
- 采用 Conventional Commits 标准:
typescript
<type>(<scope>): <subject>
# 示例
feat(user): add password reset UI
fix(router): handle 404 redirect
chore(deps): upgrade axios to 1.2.0常用 type
- feat: 新功能
- fix: Bug修复
- docs: 文档变更
- style: 代码样式调整
- refactor: 重构(不修复 bug 也不增加功能)
- test: 测试相关
- chore: 构建/工具变更