项目规范
1. 目录规范
├─apis // 接口列表
├─components // 公用组件
│ └─index.ts // 处理公用组件的全局注册事件
├─constant.ts // 全局常量
├─enums // 公共枚举
│ └─index.ts // 左侧菜单栏、流程节点的配置等
├─router // 路由相关
│ ├─moduleName.ts // 模块的路由信息
│ └─index.ts // 路由整合到处、全局路由钩子、VueRouter初始化
├─store // VueX部分
│ ├─index.ts // 全局的state和getters定义
│ ├─actions // 全局的actions方法定义
│ └─mutations // 全局的mutations方法定义
├─styles // 公用样式或者组件库重置主题样式
│ ├─assets // 静态资源(字体图标、图片、语言包等...)
│ └─other.scss // 其他项目自身使用的样式
├─utils // 工具类代码
├─views // 业务代码
│ └─module // 模块
│ ├─componets // 该模块下的业务组件
│ └─index.vue // 页面级的业务代码
文件名以短横线连接。
2. API管理
● 统一管理
● 根据业务模块进行分块(不重复定义)
● 调用底层的net方法(我们基于axios封装了一层net)
● 不在业务代码中直接调用接口
import { post } from './uilts/net';
//首页-获取各个平台数据情况
export const getPlatformCount = (data: unknown) => {
return post('/xxx/statistics/platform', data);
};
3. 全局常量
● 统一管理
● 需要注释
● 大写命名
// 存储xx对应的sessionStorage的key
export const STORAGE_KEY_FOR_XX = 'xx';
4. Style
● 业务样式使用scoped
● 尽量不写内联样式:超过3个就要提取出来
● 使用全局sass变量(目前配置我们自研组件库抛出的sass变量使用)
/* 反例 */
.text {
color: #777777;
}
.text {
color: $--xx-color-text-secondary;
}
● 采用 BEM来规范 CSS 样式命名
○ block:模块,名字单词间用 - 连接
○ element:元素,模块的子元素,以 __ 与 block 连接
○ modifier:修饰,模块的变体,定义特殊模块,以 – 与 block 连接
<div class="page-btn page-btn--mini">
<button type="button" class="page-btn__prev">上一页</button>
<!-- ... -->
<button type="button" class="page-btn__next">下一页</button>
</div>
● 规范 style 声明顺序
相关的属性声明应当归为一组,并按照下面的顺序排列。
- position-位置
- box model-盒模型
- Typographic-排版
- Visual-视觉
.graph-analysis-main {
//Position
position: absolute;
top: 0;
left: 0;
bottom: 0;
z-index: 1000;
//Box-model
diplay: block;
float: right;
width: 100px;
height: 100px;
margin: 10px;
bottom: 10px;
//Typography
font: normal 13px '微软雅黑', '楷体';
line-height: 1.5;
color: #666666;
text-align: center;
//Visual
background-color: red;
border: 1px solid #ddd;
border-radius: 3px;
}
● 若要改变第三方组件库的样式,需要加上顶级作用域。
/* 反例 */
.el-button {
width: 100px;
}
/* 推荐 */
.home .el-button {
width: 100px;
}
● 设置统一前缀,可用模块名称或者顶层类名
.home {}
.home-header {}
.home-content {}
5.业务组件内部不引用其他组件业务组件
命名规范
1. 变量命名
不要过于简单随意,命名最好带有含义,清晰明确。
// 反例
const a = [];
const arr = [];
const one = 1;
const v = 'aaa';
// 推荐
const nodeList = [];
const nodeCount = 1;
const DEFAULT_VALUE = 'aaa';
2. 方法命名
推荐格式:【动作】 + 【主体】,能一看就清楚这个方法是做什么,会返回什么信息
<template>
<el-input @change="handleChange" />
</template>
<script lang='ts'>
export default class Home {
// 反例
list() {}
del() {}
change() {}
// 推荐
getNodeList() {}
delNode() {}
addNode() {}
updateNode() {}
handleChange() {}
handleInputChange() {}
}
</script>
3. 组件名称命名
通过组件名称能明确
○ 组件使用场景,如模块内部、项目内公用
○ 组件类型,如弹窗类、卡片类。
<!-- 反例 -->
<info />
<!-- 推荐 -->
<!-- 首字母大写形式 -->
<!-- 公共组件 -->
<CommonXxx />
<!-- home.vue -->
<HomeCard />
<!-- groupList.vue -->
<GroupEditDialog />
<!-- 短横形式 -->
<!-- 公共组件 -->
<common-xxx />
<!-- home.vue -->
<home-card />
<!-- groupList.vue -->
<group-edit-dialog />
代码相关
1. 静态值统一放置或者放在文件最前面
● 当前文件使用的放置最前面
● 全局使用时单独放置全局常量值文件中 , 如src/constant.ts
2. 不直接使用枚举值
// 反例
if (this.status === 1) {}
// 推荐
enum RiskType {
BASE = 0,
ERROR = 1,
WARN = 2,
INFO = 3
}
if (this.status === RiskType.ERROR) {}
3. 解构容错处理
// 反例
const { data, total } = await getData(params);
const [first, second] = arr;
// 推荐
const { data = [], total } = (await getData(params)) || {};
const [first, second] = arr || [];
4. 数组、对象的方法属性调用
在数组、对象可能为空的情况下,调用属性或者方法需要做一些容错处理。
// 反例
data.forEach(() => {})
obj.a
// 推荐
(data || []).forEach(() => {})
(obj || {}).a
5. 重复创建新对象的情况,使用类或方法的方式创建
// 反例
let o = {
name: '',
age: 20
}
const resetO = () => {
let o = {
name: '',
age: 20,
}
}
// 推荐
class Obj {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
let o = new Obj('', 20);
const resetO = () => {
let o = new Obj('', 20);
}
// 推荐
const generateObj = (name, age) => ({
name,
age,
// other info
});
let o = generateObj('', 20);
const resetO = () => {
let o = generateObj('', 20);
}
6. 重复的判断表达式,可以单个变量指代
<template>
<div>{{ name }}</div>
</template>
<script lang='ts'>
export default class Home {
@Prop({ default: 'EDIT' }) type!: 'EDIT' | 'ADD';
// 推荐
get isEdit() {
return this.type === 'EDIT';
}
get title() {
return this.isEdit ? '编辑' | '新建';
}
save() {
const params = this.isEdit ? obj1 : obj2;
}
}
</script>
7. 函数的单一职能原则
一个函数尽量只处理一个事情,减少代码耦合。
// 反例
async getData() {
// 调用两个无前后关联的请求接口方法
apifn1(params);
apifn2(params);
}
// 推荐
async getData1() {
await apifn1(params);
}
async getData2() {
await apifn2(params);
}
Vue相关(2.x)
1. vue option放置顺序
export default class Flow extends Vue {
@Prop({ default: false }) visible!: boolean;
name: boolean = true;
computed: {},
watch: {},
// 生命周期钩子,按调用顺序编写
created() {},
beforeCreate () {},
...,
destroyed () {},
// methods方法
getList() {},
// 使用render函数时,置于末尾
render () {}
}
2. 避免在template上写太复杂的逻辑
推荐可以将比较复杂的逻辑写在计算属性中。
<template>
<!-- 反例 -->
<div>{{ bol1 ? bol2 ? : 'a' : 'b' : 'c'}}</div>
</template>
<template>
<div>{{ name }}</div>
</template>
<script lang='ts'>
export default class Home {
// 推荐
get name() {
return bol1 ? bol2 ? : 'a' : 'b' : 'c';
}
}
</script>
3. 关于后备组件的处理
对于页面中后备组件(页面初始化时不需要使用的组件,比如弹窗),建议通过v-if去减少组件实例的初始化。
<template>
<div class="page">
<!-- 反例 -->
<aaa-dialog :visible="aaaDialogVisible" />
<bbb-dialog :visible="bbbDialogVisible" />
<!-- 推荐 -->
<aaa-dialog v-if="aaaDialogVisible" :visible="aaaDialogVisible" />
<bbb-dialog v-if="bbbDialogVisible" :visible="bbbDialogVisible" />
</div>
</template>
4. 尽量不用watch去深度监听复杂对象
// 反例
export default class Parent {
setData() {
this.data[0].bol = !this.data[0].bol;
}
}
export default class Child {
@Prop() data: Base[];
@Watch('data', { deep: true })
}
// 推荐
export default class Parent {
setData() {
const data = handleData();
this.data = data;
}
}
export default class Child {
@Prop() data: Base[];
@Watch('data')
}
5. 尽量不要在computed中对属性赋值
<template>
<div>{{ name }}</div>
</template>
<script lang='ts'>
// 反例
export default class Home {
a = 1;
get name() {
this.a++;
return bol1 ? bol2 ? : 'a' : 'b' : 'c';
}
}
</script>
6. 不直接调用除公共方法之外的组件方法
// 反例
export default class Home {
fn() {
this.$refs.com.fn1();
}
}
代码性能
1. 数据分页处理
对于业务场景上不确定数据量的数据,必须进行分页。如表格、下拉框等。
2. 变量本地化
// 反例
export default class Demo extends Vue {
list: number[] = [];
name = '选项';
fn() {
for (const i = 0; i < 100; i++) {
this.list.push(this.name + i);
}
}
}
// 推荐
export default class Demo extends Vue {
list: number[] = [];
name = '选项';
fn() {
const { list = [], name } = this;
for (const i = 0; i < 100; i++) {
list.push(name + i);
}
this.list = list;
}
}
3. 非响应式数据
对于纯展示类数据或者不需要渲染在模板上的数据可以通过本地变量或者数据冻结处理,来提高性能。
// 反例
export default class Demo extends Vue {
list: number[] = [];
async getData() {
let uuid = 0;
const { data = [], total } = (await getData(params)) || {};
this.list = data.map((item) => ({
...(item || {}),
id: uuid++
}));
}
}
// 推荐
export default class Demo extends Vue {
list: number[] = [];
async getData() {
let uuid = 0;
const { data = [], total } = (await getData(params)) || {};
this.list = data.map((item) =>
Object.freeze({
...(item || {}),
id: uuid++
})
);
}
}