前端 code lint 和代码风格指南(内附最佳实践)

前言

lint 工具用来检查编程错误,最初是从 C 语言中发展起来的。在 C 语言最初时期,编译器没法捕获一些常见的编程错误,所以开发出了一个叫作 lint 的辅助程序,经过扫描源文件来查找问题。javascript

当咱们在 linting 的时候咱们到底在干什么?实际上,最终目标是但愿代码更加健壮,而且不论团队有多少成员,代码就像同一我的写出来的同样,加强可读性强。css

能够将众多 linters 的检查目标大体分为三类:html

  • programmer errors :主要是对语法的检查,这类错误会影响程序执行的正确性。
  • best practices :其目的主要是为了不出现让人困惑的代码,即便检查出问题也不必定意味着程序会执行出错,也有多是正确的,但依然会使人困惑。这一步是避免潜在的错误,以及让代码更加清晰明确。
  • style issues :主要是代码风格方面的检查,例如空格、标点符号、代码外观等等。

前端 linters 分类

JavaScript

下图展现了 JavaScript linters 的进化史:前端

JSLint

2002 年由 Douglas Crockford 建立,用来进行 JavaScript 语法检查和校验。JSLint 定义了一个比 ECMAScript 编程语言标准更为严格的子集,是一种更高的标准。JSLint 彻底是用 JavaScript 编写的。vue

JSLint 接收 JavaScript 源代码并对其进行扫描。若是发现问题,它将返回一条消息来描述问题以及源代码中的大概位置。这些问题多数时候是语法错误,但不全是语法错误,也多是代码风格和结构的问题。它不能证实程序是正确的,只是提供了一个方式来帮助发现问题。JSLint 更加关心代码质量,所以即便浏览器能够正常运行的代码,JSLint 也可能不会经过。使用 JSLint 就意味着要欣然接受它全部的建议。java

JSLint 能够对 JavaScript 源代码或 JSON 文本进行操做。 JSLint 将会承认 ES6 的一部分优秀的特性,例如 letconst 等等。react

评价

优势
  • 使用简单,开箱即用,无需再次配置。
缺点
  • 有限的配置选项,不少规则不能禁用。
  • 规范严格,凡是不承认的风格都会报一个 warning 。
  • 扩展性差。
  • 没法根据错误定位到对应的规则。

JSHint

2010年基于 JSLint 诞生了 JSHint ,它主要解决了 JSLint 过于独断的问题,提供了一些配置以及添加一些 rules 。相较之下更友好,也更容易配置,因此很快就发展了起来,也获得了众多 IDE 和编辑器的支持。git

JSHint 扫描用 JavaScript 编写的程序,并报告常见的错误和潜在的错误。 潜在的问题多是语法错误、因为隐式类型转换致使的错误、变量泄漏等。能够经过指定任意数量的 linting 选项或在源代码中声明指令来控制 JSHint 的行为。es6

JSHint 附带了一组默认的警告,但这些也是可配置的。能够在配置文件中指定要打开或关闭的 JSHint 选项。 例如,如下文件将启用有关未定义和未使用的变量的警告,并告知 JSHint 一个名为 MY_GLOBAL 的全局变量。github

{
  "undef": true,
  "unused": true,
  "globals": {
    "MY_GLOBAL": true
  }
}
复制代码

可是,因为它是基于 JSLint 开发的,天然原有的一些问题它也继承下来了,好比不易扩展,不容易直接根据报错定位到具体的规则配置等。

评价

优势
  • 能够灵活配置规则,支持配置文件
  • 支持了一些经常使用类库
  • 支持了基本的 ES6 语法
缺点
  • 不支持自定义规则
  • 没法根据错误定位到对应的规则

ESLint

2013年,Nicholas C. Zakas 建立了 ESLint ,提供了更好的 es6 支持,以及更多的 rules ,尤为是一些代码风格方面的,以及一个灵活的插件系统,可让开发者建立本身的 rules ,同时能够方便的根据报错定位到具体的规则配置。

规则的错误等级分为三级,能够更加细粒度地控制如何应用规则:

  • "off"0 - 关闭此条规则检查
  • "warn"1 - 警告,不会影响 exit code
  • "error"2 - 错误,exit code 为 1

默认状况下全部规则都是关闭的,"extends": "eslint:recommended" 会打开全部有“√”标记的规则,这些规则只跟着主版本更新,也能够在 npm 中查找以 eslint-config 开头的共享配置,经过 extends 配置项来添加。

ESLint 默认使用 Espree 做为 JavaScript 解析器,能够在 parser 配置项中更改解析器。解析器会将源代码解析成抽象语法树 AST (Abstract Syntax Tree),而后插件会根据这个 AST 来建立一些称为 lint rules 的断言,来描述代码应该是怎样的。

评价

优势
  • 默认规则里面包含了 JSLint 和 JSHint 的规则,易于迁移
  • 有三种错误等级,能够更细粒度地控制 lint 的行为
  • 灵活的插件扩展机制
  • 能够自定义规则
  • 能够根据错误定位到对应的规则
  • 支持 ES6
  • 支持 JSX
缺点
  • 更大的灵活性意味着更复杂的配置
  • 比前面两个慢

TypeScript

TSLint / typescript-eslint

用来检查 TypeScript 的,可是2019年已经废弃了,如今使用的是ESLint,配合 typescript-eslint 。TypeScript 团队也宣布将 TypeScript 代码库从 TSLint 迁移到 typescript-eslint

ESLint 和 TypeScript

ESLint 使用一个 parser 将 source code 转成抽象语法树 Abstract Syntax Tree (AST) 的数据格式,而后插件根据这个 AST 来进行 lint rules 的检查。

TypeScript 是 JavaScript 的静态代码分析器,在基础的 JavaScript 上添加了一些额外的语法。TypeScript 使用一个 parser 将 source code 转成 AST ,而后 TypeScript Compiler 的其余部分使用这个 AST 来执行其余操做,例如给出类型检查后的问题反馈等等。

然而,ESLint 和 TypeScript 使用的是不一样格式的 AST ,这就是 typescript-eslint 这个项目存在的主要缘由。typescript-eslint 就是为了可以一块儿使用 ESLint 和 TypeScript 。 TSLint 使用的就是 TypeScript AST 格式,其优势是不须要一个调和 AST 格式之间差别的工具,可是主要缺点是 TSLint 没法重用 JavaScript 生态中围绕 linting 已经作好的工做,而是从头开始从新实现全部的功能,从规则到自动修复功能等等。所以,TypeScript AST 不兼容 ESLint 用户写成并使用的 1000 多条规则。

ESLint 解析 TypeScript:@typescript-eslint/parser

因为TypeScript 是 JavaScript 的超集,它包含了全部 JavaScript 语法之外,还额外添加了一些语法,例如:

var x: number = 1;
复制代码

当 TypeScript Compiler 解析这段代码生成 TypeScript AST 时,: number 语法也会出如今语法树中,ESLint 不借助其余工具是没法理解的。但 ESLint 在设计时就考虑到了这些用例。ESLint 不只仅是一个库,而是由许多重要的移动部件组成。其中一个就是 parser 。ESLint 有一个内置的 parser 叫作 espree ,若是想支持非标准的 JavaScript 语法,只须要提供另一个 parser 给 ESLint ,它须要将 TypeScript source code 解析为 ESLint 能够兼容的 AST 。 @typescript-eslint/parser 就是这样一个自定义的 ESLint parser 的实现。流程以下:

  1. ESLint 调用 ESLint config 中定义好的 parser ( @typescript-eslint/parser )。
  2. @typescript-eslint/parser 处理全部特定于 ESLint 的配置,而后调用 @typescript-eslint/typescript-estree 。这个包只用来将 TypeScript source code 转为一个适当的 AST 。
  3. @typescript-eslint/typescript-estree 经过调用 TypeScript Compiler 将源代码生成一个 TypeScript AST ,而后将这个 AST 转换为 ESLint 须要的格式。这种 AST 格式不只仅用于 ESLint,还有更普遍的用途。它有本身的规范,也就是 ESTree@typescript-eslint/typescript-estree 还被其余工具重用,例如 Prettier 的 TypeScript 使用。

ESLint rules 兼容 TypeScript:@typescript-eslint/eslint-plugin

TypeScript 和 ESLint 有相似的目标,所以可能出现 TypeScript 解决的一些问题本来是依赖 ESLint 解决的,二者可能会不兼容,最佳的解决方式是禁用相关的 ESLint 规则,转而交由 TypeScript Compiler 。

因为 TypeScript 是 JavaScript 的超集,即便 AST 进行了转换,最终的 AST 可能还会包含一部分让 ESLint 没法理解的部分,因此有些 ESLint rules 可能没法正常工做。有几种解决方案:要么解决 ESLint rules 的兼容性,要么使用另外的规则,即 @typescript-eslint/eslint-plugin

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  plugins: [
    '@typescript-eslint',
  ],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
  ],
};
复制代码

关于 Babel 和 babel-eslint

Babel 如今支持解析 TypeScript source code 可是不进行类型检查。这是使用 TypeScript Compiler 的一个替代方法。经过插件,它一样也能够支持许多其余 TypeScript Compiler 不支持的语法。

typescript-eslint 是由 TypeScript Compiler 提供支持的。babel-eslint 支持了一些 TypeScript 自己不支持的额外的语法,可是 typescript-eslint 利用类型信息能够支持建立 rules ,而这是 babel 作不到的,由于 babel 没有类型检查。由于它们是由不一样的底层工具驱动的独立项目,因此目前不打算将它们一块儿使用。

其余

stylelint

用来检查样式,帮助避免错误和强制代码风格。能够理解最新的 CSS 语法,从 HTML 、 markdown 及 CSS-in-JS 对象和模板中提取内联的样式,能够解析类 CSS 语法,如 SCSS 、 Sass 、 Less 和 SugarSS 。支持插件,支持自定义规则。能够自动修复大多数违反代码风格的问题。而且是彻底可配置的,经过在根目录添加配置文件 .stylelintrc.json 来按需配置。

commitlint

commitlint 用来检查 commit message ,帮助团队遵照 commit 约定,同一代码提交风格。支持经过 npm 安装已有的配置,或经过配置文件定义配置。使用 husky 来添加 git hooks :

// package.json
{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}
复制代码

commit-msg 钩子会在一个新的 commit 建立时执行,经过 -E|--env 传递 husky 的 HUSKY_GIT_PARAMScommitlint ,并将其定向到相关的编辑文件。

代码风格

目前主流的代码风格主要有 Airbnb JavaScript Style GuideGoogle JavaScript Style Guide 以及 JavaScript Standard

  1. Airbnb JavaScript Style Guide (github star 数:100k):对应的 ESLint 配置:eslint-config-airbnb
  2. Google JavaScript Style Guide (25.6k):谷歌内部的 JavaScript 编码标准。eslint-config-google
  3. JavaScript Standard (24.4k):零配置,只须要运行 standard --fix 便可自动格式化代码,还能够进行代码检查。对应的 ESLint 配置:eslint-config-standard
  4. idiomatic.js (17.1k):社区版 JavaScript 编码风格指南。对应的 ESLint 配置:eslint-config-idiomatic

代码格式化

Prettier

目前最主流的是 Prettier ,它是一个独断的代码格式化工具,专一于 style issues 。经过解析代码生成 AST ,而后再按照本身的规则从新输出格式化后的代码。Prettier 执行的时机能够是在编辑器保存时、在 pre-commit hook 中或使用 CLI 工具在命令行中执行,以确保代码风格的一致。

因为历史缘由,Prettier 仍然有一小部分选项,但官方更加推崇更少的配置项,由于其初衷就是终止团队关于代码风格的争论。这些选项一部分是初期添加的,一部分是需求增大致使的,还有一部分则与兼容性有关。选项越多,越有可能在团队内部引起争论。

Prettier 仅仅对 style issues 起做用。Prettier fork 了 recast 项目,并在内部使用了 Philip Wadler 提出的算法,该算法考虑了最大行宽(line width),最大行宽影响了代码的布局和包装代码,因此决定了最终输出的代码格式,而这是其余现有的代码格式化工具所欠缺的。例如,即使 eslint 会给出超出设定的最大行宽的警告,也没法自动修复。例如以下代码:

foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
复制代码

最终想要的效果多是这样:

foo(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne()
);
复制代码

Wadler 算法是一个简单的基于约束的代码布局系统。它“测量”代码,若是代码跨越了最大的行宽,就会中断它。Prettier 禁止全部自定义样式,方法是将源代码解析成 AST 后,使用本身的规则同时考虑最大行宽,并在必要时包装代码,从新输出格式化的代码。

Prettier 和 linters 有何区别?

linters 有两类 rules :

  1. Formatting rules:例如:max-len, no-mixed-spaces-and-tabs, keyword-spacing, comma-style…
  2. Code-quality rules:例如:no-unused-vars, no-extra-bind, no-implicit-globals, prefer-promise-reject-errors…

Prettier 承担的是 Formatting rules 的工做,是一个“全自动”的风格指南。 所以,可使用 Prettier 来进行代码风格的格式化,使用 linters 来检查 bugs!

Prettier 和 linters 配合使用

因为 linters 一般会包含样式相关的规则,使用Prettier时,大多数样式规则都是没必要要的,并且更糟糕的是,它们可能与 Prettier 冲突!所以能够将Prettier用于代码格式问题,将linter用于代码质量问题。

对于 ESLint ,能够安装 eslint-config-prettier ,来关闭全部不须要的或者可能会跟 Prettier 冲突的 ESLint rules。 stylelint 同理可使用 stylelint-config-prettier

最佳实践

linter + code style + code formatter 的组合:ESLint + Airbnb + Prettier 。

具体作法:

  1. 安装 ESLint
yarn add eslint --dev
复制代码
  1. 初始化 ESLint 配置
yarn run eslint --init
复制代码

配置文件:

// .eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "plugin:react/recommended",
        "airbnb",
        "plugin:@typescript-eslint/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    }
};
复制代码
  1. 安装 Prettier
yarn add --dev --exact prettier
# 若是须要配置文件(若是须要忽略文件,则建立一个 .prettierignore 文件)
echo {}> .prettierrc.json
复制代码
  1. 设置编辑器

最好能在保存时就自动格式化代码,让开发者无需关注代码格式,强迫症必备。

WebStorm 为例:对于 2020.1 及以上的版本:Preferences | Languages & Frameworks | JavaScript | Prettier 而后启用选项 Run on save for files

image.png

如今保存时就能够自动格式化代码了。

  1. 配置 ESLint

须要安装 eslint-config-prettier ,这个包会关闭全部不须要的或可能和 Prettier 冲突的ESLint rules。

yarn add eslint-config-prettier --dev
复制代码

而后将其添加到 ESLint 配置文件中的 extends 配置项的最后,好让它可以覆盖其余配置。这里的 prettier 除了关闭一些核心的 ESLint rules,还会关闭如下插件的部分 rules:

因为这个配置只会关闭 rules,因此跟其余配置一块儿使用才有意义,注意要放到最后:

{
    "extends": [
        "plugin:react/recommended",
        "airbnb",
        "plugin:@typescript-eslint/recommended",
        "prettier"
    ]
}
复制代码

注意,还可使用 eslint-plugin-prettier 插件把 Prettier 当作一个 linter rule 来运行,但这不是官方推荐的作法,主要有如下几个劣势:

  • 编辑器中会出现不少烦人的红色波浪线,Prettier 的哲学就是让人忘记格式化,而不是给出一个提示。
  • 比直接运行 Prettier 要慢。
  • 它们只是一个间接层(one layer of indirection),有些东西可能会出问题。
  1. 添加 Git hooks

保险起见,最好在代码提交前也格式化一下。也就是能够在 pre-commit hook 里运行 Prettier 。

执行如下命令,会依赖 package.json 的 devDependencies 中的代码检查工具来自动安装并配置 huskylint-staged ,所以须要确保事先已经安装了 Prettier 和 ESlint 。

npx mrm lint-staged
复制代码

package.json 会自动添加相关配置(默认生成的配置针对 .js 文件的,能够按需改成 .ts.tsx 等),这样在每次提交前,都会经过 lint-staged 和 husky 运行 ESLint 和 Prettier 。

{
  "devDependencies": {
    "husky": ">=4",
    "lint-staged": ">=10",
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.js": "eslint --cache --fix",
    "*.{js,css,md}": "prettier --write"
  }
}
复制代码

关于 lint-staged 的做用:在代码提交以前 linting 才有意义,这样能够确保不会将糟糕的代码上传到仓库中,以及强制统一风格。可是对整个项目运行一个 lint 过程很慢,而 linting 结果多是可有可无的。因此,只须要 lint 即将要提交的文件。 lint-staged 包含一个脚本,该脚本会运行任何shell任务,将staged files 做为参数,经过一个特定的 glob pattern 进行过滤。

husky(哈士奇)主要做用是添加 git hook(git 在特定的重要动做发生时触发自定义脚本),用来阻止很差的 git commitgit push 等等。

参考文献

  1. cloud.tencent.com/developer/a…
  2. www.youtube.com/watch?v=kuH…
  3. A Prettier JavaScript Formatter
相关文章
相关标签/搜索