# 编码规范
文档维护人: 赤红(yangjh@authine.com)、木木(linqh@authine.com)
# 格式规范
# 缩进
使用两个空格;
# 单行长度
不超过80字符;
# 分号
为了避免有些情况加分号,有些情况不加分号,建议语句行尾都加分号;
# 引号
统一在js/ts中使用单引号,html文件中使用双引号;
# 空格
- 运算符两边需加一个空格;
- 关键字后边加一个空格, 若关键字不打头,前面加空格;
- 代码块前面加一个空格;
- 非行末尾的逗号后面加一个空格(数组,函数参数)
- 冒号后面加一个空格
- 单行注释与代码之间一个空格;
- 多行注释*后面一个空格;
- 行末尾不能有空格;
/**
* 这是多行注释(*后面有个空格)
* 下面的单行注释//前后各一个空格
* 运算符空格
*/
let a = 2; // 赋值运算符两边一个空格
let b = (a + 5) * 2 - 100; // 算数运算符两边一个空格
let c = a > b && b <= 10; // 逻辑运算符两边一个空格
let d = b > 5 ? true : false; // 三目运算符两边一个空格
/*
* 关键字空格
* if,else,for, function while,do,switch,case,try,catch,finally,with,return,typeof等关键字后
*/
function f(a, b, c) { // 函数声明参数逗号(,)后加空格,代码块`{`前
if (a > b) { // if关键字后空格
c = a - b;
} else { // else 不在行开头,前后都加空格
c = b - a;
}
return c;
}
for (let i = 0; i < 2; i++) {}
function func(a, b, c) { } // 函数声明参数逗号(,)后加空格,代码块`{`前
const obj = { // 对象属性key的冒号后
a: 2,
b: true
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 注释
使用
/**...*/
进行多行注释使用
//
进行单行注释注释离左侧的
//
、*
一个空格在
/** */
包裹的注释中使用@author
、@description
,@todo
,@deprecated
等进行提醒注释,参照jsdoc注释规范函数注释应当更加规范,至少包含函数描述、参数、返回值
/**
* jsdoc 注释规范
* @description 函数等描述
* @param {string} p1 参数1的说明
* @param {string} p2 参数2的说明,如果比较长
* 那就换行了.
* @param {number=} p3 参数3的说明(可选)
* @return {Object} 返回值描述
*/
function foo() {
console.log('step 1'); // 这是单行注释
// 单独一行的注释
console.log('step 2');
/** @deprecated 这个 方法已经废弃 */
console.log('will be delete');
/** @todo 将来这里应该更换为函数调用 */
console.log('will be invoke func');
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
文档注释
文件注释用于告诉不熟悉这段代码的读者这个文件中包含哪些东西。 应该提供文件的大体内容, 它的作者, 依赖关系和兼容性信息。
/** * @fileoverview Description of file, its uses and information * about its dependencies. * @author xxx@yyy.com (Firstname Lastname) * @copyright 2019 Authine Inc. All Rights Reserved. */
1
2
3
4
5
6
# 命名规范
# 变量、函数命名
命名应有意义,有可读性,不可用单字母
命名单词用英文,不要用拼音或英文拼音混杂
不知道怎么命名? 英文不好?看这里
当命名有冲突时,应通过意义或场景加以区分,切忌
temp1
,temp2
,temp3
....
// bad
const r = 3;
// good
const result = 3;
// bad
function guanbi() { }
// good
function close() { }
2
3
4
5
6
7
8
9
属性、变量、函数、对象实例使用驼峰法(
camelCase
),例如tableData
、isVisible
;构造函数、类或枚举常量使用Pascal命名法(
PascalCase
),例如HomeController
、GlobalState
;不要滥用
_
、$
等字符(在vue
里面state
以_
开头的变量不会给双向绑定响应);缩写使用全大写或全小写
// bad
const Data = {};
function GetData() { }
// good
const data = {};
function getData() { }
// bad
function homeController() { }
HomeController.prototype.GetData = function() {};
class globalState { }
// good
function HomeController() { }
HomeController.prototype.getData = function() {};
class GlobalState { }
// bad
enum instanceState {
active = 1,
done = 1,
}
// good
enum InstanceState {
Active = 1,
Done = 1,
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 文件命名
文件名、文件夹名全部小写,用短接线相连-
,特殊文件例如配置文件,可能会出现以.
相连的情况;
// bad
// FormDropdownList.js
// good
// form-dropdown-list.js
// bad
// StartUp.js
// good
// start-up.js
2
3
4
5
6
7
8
9
# 组件命名
组件命名采用Pascal命名法(PascalCase
),例如LeftSidebar
、Header
;
组件在模板中使用时,如果是基础组件,可使用短接线方式使用,如果是同级业务组件,使用PascalCase
的命名方式调用;
<template>
<div class="app-form">
<FormHeader></FormHeader>
<a-input></a-input>
<a-button></a-button>
</div>
</template>
<script lang='ts'>
import { Component, Vue } from 'vue-property-decorator';
import { Input, Button } from 'h3-antd-vue';
import FormHeader from './form-header.vue';
// bad: import formHeader from './form-header.vue';
@Component({
name: 'AppForm',
components: {
AInput: Input,
AButton: Button,
FormHeader
}
})
class AppForm extends Vue {}
// bad: class appForm extends Vue {}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 特定类型规范
# 变量声明
声明尽量提前,避免变量提升带来代码阅读和维护问题
常量用
const
、变量用let
不要用一个
let
或const
同时作用于多个变量// bad const a = 1, b = 2, c = 3; /** * 1、从末尾添加变量麻烦 * 2、容易发生下面的错误: * const a = 1, * b = 2; * c = 3; */ // good const a = 1; const b = 2; const c = 3;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const
、和let
分组申明,不要交叉不要使用链式赋值
// bad
let a = b = c = 1;
// good
let a = 1;
let b = 1;
let c = 1;
2
3
4
5
6
# 字符串相关
使用单引号
通过代码构造字符串时使用es6模板字符
// bad function getStr(name) { return 'How are you, ' + name + '?'; } // good function getStr(name) { return `How are you, ${name}?`; }
1
2
3
4
5
6
7
8不要调用函数
eval(str)
# 对象相关
使用字面量创建对象
const item = new Object(); // bad const item = {}; // good
1
2method
使用简写形式const obj = { value: 1, bad: function (val) {}, // bad good (val) {} // good };
1
2
3
4
5属性名为变量时使用简写形式
const keyName = 'keyName'; // bad const obj = { keyName: keyName }; // good const obj = { keyName };
1
2
3
4
5
6
7
8
9只有属性名包含特殊字符(空格 -)等时才使用单引号
// bad const bad = { 'foo': 3, 'bar': 4, 'data-info': 5 }; // good const good = { foo: 3, bar: 4, 'data-info': 5 };
1
2
3
4
5
6
7
8
9
10
11
12不要直接调用
Object.prototype
上的方法,如hasOwnProperty
,isPrototypeOf
等【因为prototype
上的方法可能被覆写,另外通过Object.create(null)
创建的对象prototpye
为null
】const obj = { name: 'foo' }; const key = 'name'; // bad console.log(obj.hasOwnProperty(key)); // good console.log(Object.prototype.hasOwnProperty.call(object, key)); // 也推荐用 ES6的 Reflect来操作对象,该 API规避了一些旧有的问题
1
2
3
4
5
6
7
# 数组相关
使用字面量创建数组
// bad const arr = new Array(); // good const arr = [];
1
2
3
4使用扩展运算符
...
实现数组拷贝、实现遍历器对象转换到数组,注意:...
实现的是浅复制// 拷贝数组 const origin = [1, 2, 3, 4, 5]; // good const copy = [...origin]; // 转换实现遍历器对象 const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo];
1
2
3
4
5
6
7
8
9
10
11
12使用
Array.from
转换类数组对象// 转换类数组 const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; // bad const arr = Array.prototyp.slice.call(arrLike); // good const arr = Array.from(arrLike);
1
2
3
4
5
6map
/filter
/some
/reduce
等方法,不是数组遍历的方法,不要作为遍历函数使用;这些方法内的回调函数需要有return
值;
// bad - map不是用来遍历的方法,内部有返回
const origin = [1, 2, 3, 4];
[1, 2, 3, 4].map((i) => {
console.log(i);
});
// good
[1, 2, 3, 4].forEach((i) => {
console.log(i);
});
// bad - no returned value means `acc` becomes undefined after the first iteration
// bad - acc的值,默认可以通过第三个参数传进去
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
return acc.concat(item);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 函数相关
- 不要在(
if
/for
/while
等)非函数块声明函数,可以声明函数表达式
// bad
if (isVisible) {
function test() {
console.log('Nope.');
}
}
// good
if (isVisible) {
test = () => {
console.log('Yup.');
};
}
2
3
4
5
6
7
8
9
10
11
12
13
- 使用
...
运算符而不是arguments
// bad arguments是类数组
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good args是真正的数组
function concatenateAll(...args) {
return args.join('');
}
2
3
4
5
6
7
8
9
10
- 使用默认参数而不是
opts = opts||{}
// really bad
function handleThings(opts) {
// 1、有副作用(修改arguments)
// 2、如果调用者本身传递的是false或者0时,莫名其妙变成了{}
opts = opts || {};
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
2
3
4
5
6
7
8
9
10
11
12
- 默认参数总是放在最后
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
2
3
4
5
6
7
8
9
不要修改参数(应该当成
const
定义的常量的不可变特性对待,但const
在 js 中并非完全不可变!)形参或实参多行时右括号)左对齐
// bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27需要匿名函数的地方,使用箭头函数,但是切莫滥用箭头函数,特别需要考虑
this
的时侯
# 模块规范
- 使用es6模块规范(
import/export
),不要使用不要混用commonjs规范(require
)以及其他模块规范;
// bad
const xxx = require('./xxxx');
import yyy from './yyy';
// good
import xxx from './xxxx';
import yyy from './yyy';
2
3
4
5
6
7
- 不要仅作为执行脚本导入,在webpack中这种情况被定义为副作用(SideEffect); 可查阅webpack的Tree Shaking
// bad
import './xxxx';
// good
import xxx from './xxxx';
2
3
4
5
- 注意导入顺序,第三方依赖先导入,本地项目依赖后导入, 尽量分组导入, 代码读起来更整洁;
// bad
import h3Utils from './utils/';
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import AppHeader from './app-header.vue';
import _ from 'lodash';
// good
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import _ from 'lodash';
import h3Utils from './utils/';
import AppHeader from './app-header.vue';
2
3
4
5
6
7
8
9
10
11
- 属于同一个模块的,内部实现的只导入一次(都用到的情况下统一导入)
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// or
import foo, {
named1,
named2,
} from 'foo';
2
3
4
5
6
7
8
9
10
11
12
- 不要导出可变绑定
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
2
3
4
5
6
7
- 单个导出时加
default
,默认导出方式可以自定义名字方式引入
// bad
export function() {}
// good
export default function() {}
//加入导出文件为 a.js,可以这样使用
import cc from a
2
3
4
5
6
7
8
# 关于样式
# 项目结构
项目分模块划分
src
├── common # 项目公共资源
│ ├── assets # 全局静态资源,包括图片、字体等
│ ├── components # 全局公共业务组件
│ ├── config # 全局配置,放置一些常量或枚举、或环境初始化,类库的初始化
│ ├── directives # 全局指令
│ ├── service # 公共模块接口
│ ├── styles # 公共样式文件
│ ├── lib # 全局公共库文件(本地库与npm库之间的一层缓冲,理论上可以提取到npm中)
│ ├── utils # 全局公共工具函数库,(本地工具库与npm库之间的一层缓冲,理论上可以提取到npm中)
│ ├── typings # # 全局typescript声明文件
├── wrapper # 项目入口
│ ├── components # 全局公共业务组件
│ ├── layout # 布局组件
│ ├── router # 路由配置文件,聚合其他模块
│ ├── store # 全局store,需要聚合其他模块的store
│ ├── entry # 项目入口文件,例如main.ts, App.vue, index.html
│ ├── typings # 全局typescript声明文件
├── 【moudle】 # 业务模块
│ ├── components # 当前业务模块组件,只在本模块内调用
│ ├── router # 路由配置文件,被wrapper中的主路由引用;(若模块是纯单页,则不需routers)
│ ├── store # 模块的vuex store,被wrapper中的主store引用;
│ ├── entry # 模块入口文件,可能是main.ts, App.vue, index.html;也可能是一个entry.vue
│ ├── styles # 当前模块样式文件
│ ├── service # 当前模块内的接口文件
│ ├── lib # 当前模块引用的库
│ ├── utils # 当前模块引用的工具类
│ ├── typings # 当前模块typescript声明文件
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28