兴趣部落的前端代码规范实践
刚加入团队时,团队中没有统一的代码规范,虽然大多数人都有良好的代码风格,但毕竟习惯还是有差异的,导致代码中存在很多风格不一致的情况,所以我们便开启了统一团队代码风格的尝试。下面将这个过程做一个整理与总结。
ESLint
业界主要使用四种 JavaScript 代码检查工具:JSLint、JSHint、JSCS 和 ESLint,那为什么选择 ESLint 呢?可以参考文章 A Comparison of JavaScript Linting Tools 来比较这四种工具的优缺点,总的来说,我们主要看中 ESLint 的以下优点:
规则全面,且每个规则都可以配置;
支持 ES6 和 JSX;
输出结果易于理解。
新建 .eslintrc-alloy.js
module.exports = { parser: 'babel-elint', // 在 ESLint 中使用 Babel parser parserOptions: { // 解析器选项 ecmaVersion: 2017, // 默认 5,表示支持 ES5 语法。2017 表示 ES2017 sourceType: 'module', // 默认 ‘script’,‘module’ 表示代码是 ECMAScript 模块 ecmaFeatures: { // 想使用的额外的语言特性 experimentalOjbectRestSpread: true, // 启用对实验性的 object rest/spread properties 的支持 jsx: true // 启用 JSX } }, env: { // 指定启用的环境 browser: true, node: true, commonjs: true, es6: true }, plugins: [ 'react' ], root: true, // 避免 ESLint 在父级目录找配置信息 rules: { ... } };
在项目根目录下新建 ".eslintrc-alloy.js" 文件,它是我们团队代码规范的核心,将之单独抽成一个文件的目的一方面是为了便于管理团队整体的代码规范,另一方面是为了后期的开源。
迁移 ESLint Rules
将官网所有的 Rules 都放入到 ".eslintrc-alloy.js" 文件的 rules 中,这就是一个需要细心和耐心的时候了。每条 rule 的值都是以下之一:
off
或0
:关闭规则。为什么用两种方式来表示呢?一开始的时候使用数字,后来发现语义化很差,就换成了字符串,为了向前兼容便保留了数字,现在推荐使用字符串;warn
或1
:检查出不符合规范的代码提示警告,一般不用这个,因为程序员经常忽视 warning,[摊手];error
或2
:检查出不符合规范的代码提示错误。
除了配置这三个值外,有的 rule 还有 options,所以还需要额外的配置。比如第二条 rule getter-return
有个 allowImplicit
的选项,表示是否可以返回 undefined
,所以写法就是:
'getter-return': [ 'error', { 'allowImplicit': false // 不允许返回 undefined } ]
ESLint 配置 rule 的方式有些奇怪,rule 的值既可以是 String('off'/'warn'/'error'),也可以是 Number(0/1/2),同时也可以是 Array,如果是 Array 的话,第 0 位表示规则的关闭/警告/开启,同前面一样,第 1 位及后面可以是个 Object,也可以是个 String。Object 的例子如上面的 getter-return
选项。String 的例子如第五条 rule no-cond-assign
,如果写成:
'no-cond-assign': [ 'error', 'always' ]
那么就表示任何时候都不能在条件语句中使用赋值语句。具体什么时候用 Object 时候用 String,需要查看文档,看这条 rule 支持哪种写法。
就这样,对照文档,将每一条规则以及规则的配置信息都一一写入 ".eslintrc-alloy.js" 文件中,规则的选项配置需要根据团队的实际情况确定,如果有一些拿捏不准的,需要开会由小组成员共同确定,这毕竟以后就成为团队的代码规范。
代码改造
有了 “.eslintrc-alloy.js” 文件后,就有了团队的代码规范,代表着将来的代码都要以此规范为准,那现有项目的代码怎么办呢?修改呗!但问题来了,项目那么大,代码那么多,从哪里开始?一天全部改完?改不完咋办?你会发现有各种各样的问题,下面就跟大家分享一下我们团队代码改造的过程。
新建 .eslintrc.js
module.exports = { extends: [ './.eslintrc-alloy.js' ], globals: { $: false, ... }, rules: { ... } };
在项目根目录下新建 “.eslintrc.js” 文件,继承同目录下的 ".eslintrc-alloy.js",这个文件最终确定项目要遵守哪些规范。我们改造的过程大概是先在 “.eslintrc.js” 文件中将所有 rules 都设为 off
,然后一条一条删掉,删一条就找到所有不符合这个规则的代码然后修改,具体过程如下。
迁移 ESLint Rules
这个步骤跟把 Rules 迁移到 ".eslintrc-alloy.js" 文件中很像,只不过这次是将 rules 全部迁移到 ".eslintrc.js" 中,并且将所有的 rule 的值都赋为 0,即关闭所有规则,这样一来就跟一开始没有 ESLint 一样了。
构建工具
因为我们项目采用 grunt 构建项目,所以我们使用 grunt 写了一个任务,执行 grunt eslint
时,可以输出整个项目中不符合 ESLint 规范的代码所在文件及行数。
逐步删除 Rule
下面才真正开始代码改造。每次删除一个 ".eslintrc.js" 中的 rule,因为继承了 ".eslintrc-alloy.js" 的缘故,就等同于开启了这条规则,然后执行 grunt eslint
,找到不符合这条规范代码的文件以及行数,一一修改。修改的时候,不同情况要不同对待。修改一个规则后,要及时提交,那么后面的人就可以使用这条规则了。
--fix
:看官网 Rules 列表,你会发现第二列,有的 rule 有“扳手”图标,那代表工具可以自动修复,比如第一个有“扳手”的 rule:no-debugger
,当执行grunt eslint
之后,所有的debugger
都会被删除,当然前提是你在 grunt 的 eslint 任务中开启自动 fix 的功能;代码简单:比如
no-console
规则,不允许在程序中使用console.log
、console.error
等语句,同时没有--fix
,所以只要自己手动地“无脑删”即可;代码复杂:有些地方代码逻辑很复杂,这时就不能“无脑改”,你需要搞懂上下文的含义,比如
no-eval
,执行了grunt elsint
后发现整个项目中就 5 处左右使用到 eval,但每一处使用场景不一样,有的是为了在低版本机器中兼容JSON.parse
用来将字符串转成 JSON 格式,有的是为了将字符串转成正则,所以你只有对上下文很熟悉你才能修改,不然出一点小差错可能影响的是数百万甚至数千万用户。如果实在看不懂原来代码或者修改风险很大,那么最好在文件头部加上 ESLint 的注释,在当前文件中关闭这条规则;文件较多:有的规则一开启,你会发现有数百文件、上千行代码报错,比如
no-var
,这种情况下影响修改最关键的因素就是时间了,修改一条规则可能要花好几天,那怎么做呢?一种做法是在 ".eslintrc.js" 中保留这条规则的关闭状态,只有在执行grunt eslint
时开启,找到不符合规范的代码后,立刻再次关闭这条规则,然后一条一条地慢慢改,改不完也不要紧,因为这条规则没有开启。我们采取的是另一种做法,在 ".eslintrc.js" 中删除这条规则,即开启了这条规则,然后修改不符合规则的代码,对于来不及修改的代码我们写了脚本将含有不符合规范的文件头部加上 ESLint 注释,关闭规则,下一次再删除头部的注释,然后修改不符合规范的代码。这样做的好处是可以及时将规则开启,避免其他人再次增加新的不符合规范的代码;其他:其他代码难度中等、错误个数中等的也要好好改。
将以上步骤做完改造工作也基本结束了。
代码维护
经过代码改造,这时从远程仓库拉下来的代码都是符合代码规范的了,那如何保证新增的代码也全都合格呢?我们主要做了两件事情,一件是推荐大家使用最新的编辑器,比如 Atom 或者 VSCode,两者都有丰富的插件帮助你在写代码过程中就能发现哪些地方不符合规范。另一件事是我们写了一段脚本,在代码 push 到远程仓库时会先运行这段脚本来检查代码的规范性,如果发现有一处不符合规范,则终止上传过程,同时会有错误提示。
经过改造过的代码更加赏心悦目可维护性也高不少,如果你的团队也有同样的问题,那也来尝试改造一下吧。