# 编码规范

文档维护人: 赤红(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
}
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

# 注释

  • 使用/**...*/进行多行注释

  • 使用// 进行单行注释

  • 注释离左侧的//*一个空格

  • /** */包裹的注释中使用@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');
}
1
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() { }
1
2
3
4
5
6
7
8
9
  • 属性、变量、函数、对象实例使用驼峰法(camelCase),例如tableDataisVisible

  • 构造函数、类或枚举常量使用Pascal命名法(PascalCase),例如HomeControllerGlobalState

  • 不要滥用_$等字符(在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,
}
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
1
2
3
4
5
6
7
8
9

# 组件命名

组件命名采用Pascal命名法(PascalCase),例如LeftSidebarHeader;

组件在模板中使用时,如果是基础组件,可使用短接线方式使用,如果是同级业务组件,使用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>
1
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

  • 不要用一个letconst同时作用于多个变量

    // 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
    15
  • const、和let分组申明,不要交叉

  • 不要使用链式赋值

// bad
let a = b = c = 1;
// good
let a = 1;
let b = 1;
let c = 1;
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
    2
  • method使用简写形式

    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)创建的对象prototpyenull

    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
    6
  • map/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);
});
1
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.');
  };
}
1
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('');
}
1
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 = {}) {
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
  • 默认参数总是放在最后
// bad
function handleThings(opts = {}, name) {
  // ...
}

// good
function handleThings(name, opts = {}) {
  // ...
}
1
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';
1
2
3
4
5
6
7
  • 不要仅作为执行脚本导入,在webpack中这种情况被定义为副作用(SideEffect); 可查阅webpack的Tree Shaking
// bad
import './xxxx';

// good
import xxx from './xxxx';
1
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';
1
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';
1
2
3
4
5
6
7
8
9
10
11
12
  • 不要导出可变绑定
// bad
let foo = 3;
export { foo };

// good
const foo = 3;
export { foo };
1
2
3
4
5
6
7
  • 单个导出时加default,默认导出方式可以自定义名字方式引入
// bad
export function() {}

// good
export default function() {}
//加入导出文件为 a.js,可以这样使用
import cc from a

1
2
3
4
5
6
7
8

# 关于样式

  • 采用less或css
  • 类命名风格采用BEM
  • 全局样式统一放到src/styles

# 项目结构

项目分模块划分

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声明文件
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
28