提升项目可维护性的一些习惯

软件生命周期中80%的成本消耗在了维护上

《Java语言编码规范》

在前端编码时,经常遇到多人协作的情况,一些工具可以很好地提升代码维护成本。这里把最近的学习中遇到的几个分享在下面。

EditorConfig

EditorConfig是一套在编辑器间统一代码格式的解决方案。一个EditorConfig项目由.editorconfig自定义文件格式。相应的编辑器插件会按照配置文件格式化文档。

EditorConfig的语法类似.gitignore,比较好理解。下面是官网给出了规定Python和JavaScript文件格式的.editorconfig文件样例

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
29
30
31
32
33
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

不过通常的项目用不到这么复杂的配置文件。这里是Angular的配置文件,这里是曾经Vue.js的配置文件。官网给出了完整的使用EditorConfig的工程列表

存放位置

打开一个文件时,EditorConfig插件会去打开文件的目录和其每一级父目录查找.editorconfig文件,直到有一个配置文件root=true

读取顺序从上到下,路径最短的文件最后被读取,优先级最高。

关于文件格式

EditorConfig文件使用INI格式,目的是可以和Python Config Library兼容。每个分段(原文:‘section’)由一个globs)开头。斜杠(/)作为路径分隔符,#或者;作为注释。注释应该单独占一行。EditorConfig文件使用UTF-8格式、CRLF或LF作为换行符。

通配符

EditorConfig目前支持下面这些通配符:

  • * 匹配除/之外的任意字符串
  • ** 匹配任意字符串
  • ? 匹配任意单个字符
  • [name] 匹配name字符
  • [!name] 匹配非name字符
  • {s1,s3,s3} 匹配任意给定的字符串(0.11.0起支持)
  • {num1..num2}匹配num1num2间的整数

最后特殊字符可以用\转义.

属性

目前普遍支持的属性包括下面这些:

  • root:表明是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件。
  • indent_style:可以选择tab或space
  • indent_size:设置整数表示规定每级缩进的列数或空格数。如果设定为tab,则会使用tab_width的值(如果已指定)。
  • tab_width:设置整数用于指定替代tab的列数。默认值就是indent_size的值,一般无需指定。
  • end_of_line:定义换行符,支持lf、cr和crlf。
  • charset:编码格式,支持latin1、utf-8、utf-8-bom、utf-16be和utf-16le,不建议使用uft-8-bom。
  • trim_trailing_whitespace:设为true表示会除去换行行首的任意空白字符,false反之。
  • insert_final_newline:设为true表明使文件以一个空白行结尾,false反之。

支持情况

目前已有大量的IDE或文本编辑器支持EditorConfig配置。有些不需要下载插件,有些则需要。详情可参见官网

eslint

ESLint是非常流行的一个JavaScript代码检查器。便于在运行前检查出代码中潜在的错误。它的作者是Nicholas Zakas,红宝书的作者。网站也有中译版

安装

安装eslint前,需要有node.js的环境,之后通过npm安装即可

1
npm install -g eslint

当然也可以本地安装

1
npm install eslint --save-dev

配置

安装完成后,需要在项目目录下生成.eslintrc配置文件才可以使用eslint命令。这一步可以通过eslint --init按着引导完成,也可以根据自己需要修改。eslint推荐使用了一些规则,可以通过下面这样开启(extends的属性还可以是all,即启用所有规则,不推荐使用):

1
2
3
4
{
"extends": [ "eslint:recommended" ]
...
}

配置项里,还可以通过env指令代码环境,像下面这样:

1
2
3
4
5
6
{
"env": {
"browser": true,
"node": true
}
}

同样,具体的规则也是可以配置的,每个规则的配置项都有一个默认值,规则键对应的值为数值时,是下面的意思

  • 0 Disable the rule
  • 1 Warn about the rule
  • 2 Throw error about the rule

对应的值为数组时,则会更改规则配置项的原默认值,如下面例子中的quote规则:

1
2
3
4
5
6
7
8
{
"rules": {
// 使用默认的分号规则,违背时会有警告消息
"semi": 1
// 使用双引号包裹字符串,违背是会抛出错误
"quotes": [2, "double"],
}
}

关于eslint的更多配置项,可参考官网

若项目中使用到了ES6语法,则还需要安装babel-eslint包,并指定.eslintrcparserparseOptions两项。具体的配置大概是下面这样:

1
2
3
4
5
6
7
8
{
"extends": [ "eslint:recommended" ],
"parser": "babel-eslint",
"parserOptions": {
ecmaVersion: 6
},
...
}

ESLint 支持几种格式的配置文件:

  • JavaScript - 使用.eslintrc.js然后输出一个配置对象。
  • YAML - 使用.eslintrc.yaml.eslintrc.yml去定义配置的结构。
  • JSON - 使用.eslintrc.json去定义配置的结构ESLint的JSON文件允许JavaScript风格的注释。
  • Deprecated - 使用.eslintrc,可以使JSON也可以是YAML。
  • package.json - 在package.json里创建一个eslintConfig属性,在那里定义你的配置。

如果同一个目录下有多个配置文件,ESLint只会使用一个,优先级是上面列表从上到下的顺序。

注释

可以在文件中书写注释在运行时更改eslint的配置(实际上几乎所有的配置项都可以在注释中通过eslint-xxx这样的形式修改)。

当文件中出现已考虑到的规则例外时,可以通过/*eslint quotes: ["error", "double"]*//*eslint eqeqeq: 0, curly: 2*/这样的形式临时添加例外。

当文件出现不想被检测到的规则例外时,可以通过/*eslint-disable*//*eslint-enable*/避免警告。单行例外可以使用/*eslint-disable-line*/更详细的配置可以参见文档

sublime插件

上面说的这些工作,在配置完成后,需要在命令行中通过eslint xxx.file这样的形式lint。借助编辑器的插件可以获得可视化的lint结果,妈妈再也不用担心我的找不到错误了(误)。因为个人原因,下面仅以sublime为例。

下载eslint for sublime插件前,需要下载Sublime-Linter。因为前者利用了后者作为lint的平台。在Ctrl+Shift+P找到Package Controll: Install Packages后(什么?你没有装Package Control?),搜索Sublime-Linter下载安装即可。完成后,可以在Prefences -> Package SettingsTools选项卡中找到Sublime Linter的身影。

之后同样的方式搜索Sublime-contrib-eslint下载安装即可。建议在安装前去官网看看,避免遇到不必要的问题。

这些工作完成后了,可以选择SublimeLinter的mode为load/save,之后在文件载入和保存时都会对文件进行lint操作,并将违背规则的地方标出。

Commit message规范

git每次修改后需要填写commit message才能提交。这一步可以通过给git commit添加-m参数完成,像下面那样,也可以在git commit打开的vi界面下填写多行文本。

1
git commit -m 'some commit message'

git并没有对commit message的风格做出规范,可以用中文,可以用英文,甚至当你不知道该写些什么的时候,还可以去某些网站参考。

但是在团队协作中,还是建议清晰明了地书写此次commit的目的和做的修改。实际上,commit message规范这种事一直在做。比如egg.js, Angular或者更加简洁的规范:这样这样。其中Angular的规范应用较广,还有commitizen工具帮助生成changelog和检查commit message样式。

格式

根据Angular的规范,commmit message包括三个部分:Header, BodyFooter。其中Header是必需的,Body和Footer则不是。模板像下面这样:

1
2
3
4
5
<type>(<scope>): <subject>
// 空行
<body>
// 空行
<footer>

模板中,type为提交commit的类型,只有下面这些选择:

  • feat: 新功能
  • fix: 修复问题
  • docs: 修改文档
  • style: 修改代码格式,不影响代码逻辑
  • refactor: 重构代码,理论上不影响现有功能
  • perf: 提升性能
  • test: 增加修改测试用例
  • chore: 修改工具相关(包括但不限于文档、代码生成等)
  • deps: 升级依赖

其中前两种commit一定会出现在changelog中。

scope为修改文件的范围(包括但不限于doc, middleware, core, config, plugin);subject用一句话清楚的描述这次提交做了什么,首字母小写;body作为subject的补充,增加原因和目的等具体内容,可以不写。

footer部分中,当有非兼容修改(Breaking Change)时必须在这里描述清楚,或者描述关联issue。下面是一个完整的例子:

1
2
3
4
5
6
7
8
fix($compile): [BREAKING_CHANGE] couple of unit tests for IE9
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Document change on eggjs/egg#123
Closes #392
BREAKING CHANGE:
Breaks foo.bar api, foo.baz should be used instead

代码用于撤销此前commit所做修改时,message用revert开头,后面跟着被撤销commit的Header。像下面这样:

1
2
3
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.

如果当前commit与被撤销的commit,在同一个发布(release)里面,那么它们都不会出现在Change log中。

Commitizen

Commitizen就是方便你做出上面提交的工具,可以通过npm安装。

1
npm install -g commitizen

安装完成后,使用git cz代替git commit命令来提交改动。之后会出现指引帮助你完成一次合格的提交。

commitizen的插件cz-conventional-changelog可以帮助我们完成commit message,首先通过下面的命令安装并配置cz-conventional-changelog。

1
2
npm install -g cz-conventional-changelog
echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc

之后运行下面的命令即可

1
commitizen init cz-conventional-changelog --save-dev --save-exact

参考