彻底搞懂 ESLint 与 Prettier 之间的爱恨情仇

6,262 阅读9分钟

NPM 插件 ESLint

ESLint 是一个 javascript 代码检测工具,例如未使用的变量,未定义的引用,比较时使用三等,禁止不必要的括号 等等代码质量检测, 其出现大大提高了团队协作的代码规范统一性,以及个人的代码质量,深受 web 开发者们的青睐。但是其众多的配置参数,语法规则,也同样让人迷糊。好在各大 web 框架的脚手架中都内置了 ESLint 插件,渐渐的像 ESLint 看齐,开发者们无需再过多手动配置,即可享受 ESLint 带来的团队及个人效益。但是考虑到并不是所有的框架都内置了并且有许多像我一样爱折腾的朋友们,我们还是需要对其一探究竟。

话不多说,我们直接上代码。

NPM ESLint 命令行

mkdir eslint-app && cd eslint-app                // 创建空目录并进入目录
npm init                                         // 初始化 npm
touch index.js                                   // 创建入口文件
npm i eslint -D                                  // 安装 eslint 依赖

我们创建了一个新的目录,并且安装了好了 ESLint ,接着我们在 index.js 中写入代码

const hello = 'hello';

package.jsonscripts 选项中加入一条检测脚本,代表我们要对 目录下所有的 js 文件进行 ESLint 检测

"scripts": {
  "lint": "eslint ."
}

紧接着执行 npm run lint,不出意外的话,应该会出现一个报错,提示我们需要一个 ESLint 配置文件

因此,我们先在根目录下手动创建一个 eslint 配置文件,命名可以是 .eslintrc.json,eslintrc.js.eslintrc.yaml,甚至配置在 package.json 中,我这里使用了 .eslintrc.json,并添加了以下配置代码

{
  "env": { // 全局环境变量
    "browser": true // 浏览器环境
  },
  "extends": "eslint:recommended", // 继承 eslint 默认配置
  "parserOptions": { // 指定 javaScript 语言类型和风格
    "sourceType": "module" // 导入方式设定为模块导入
  },
  "rules": { // 配置规则
   "no-unused-vars": 1// 警告不允许出现未使用的变量
   "semi": [1, "always"] // 警告强制加上句尾分号
  }
}

.eslintrc即将废弃,可以使用 .eslintrc.json 完全代替

配置文件已经创建好了,我们继续执行之前的 npm run lint,如果你的 index.js 代码和我上述一样,此时终端应该出现了警告,告诉我们 index.js 定义了 hello 变量但没有使用。至此我们第一步的校验已经大功告成

许多朋友已经养成了不写分号的习惯但团队又必须要求加上句尾分号,这个时候可以用到 ESLint 自带的格式化功能,他能对我们一些比较基本的语法问题进行自动格式化,在 package.json scripts 中添加一条格式化脚本,可以看到与 lint 的区别就是加了一个 --fix 参数

"scripts": {
  "format": "eslint . --fix"
},

紧接着我们去掉 index.js 第一行的分号,执行 npm run format,然后回头看 index.js, 应该发现句尾已经自动帮我们加上了分号了

eslint-webpack-plugin(eslint-loader)

自从 webpack 5 开始,eslint-loader 已经不再被 webpack 推荐,具体原因见 github.com/webpack-con…

那么问题来了,ESLint 如何与现有项目进行融合?以及在实际项目中,每次如果需要手动执行一次 lint 才能看到我们的语法错误,那岂不是是十分麻烦的?

这个时候 eslint-webpack-plugin 派上用场了,eslint-webpack-pluginwebpack 插件,在项目编译期间,能对我们的 js 代码进行实时的检测。我们先手动搭建一个 webpack 环境,在此项目上继续安装依赖

npm i webpack-cli webpack webpack-dev-server webpack-html-plugin eslint-webpack-plugin -D

package.json scripts 中添加一条启动脚本

"scripts": {
  "start": "webpack serve"
}

并在根目录添加 webpack.config.js 配置文件

const HtmlWebpackPlugin = require('html-webpack-plugin');
const EslintWebpackPlugin = require('eslint-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './index.js',
  devtool: 'inline-cheap-module-source-map',
  plugins: [new HtmlWebpackPlugin(), new EslintWebpackPlugin()],
  devServer: {
    port: 9000,
  },
};
  • 手动修改 index.js 去掉句尾分号,随即运行 npm start,观察控制台会发现出现了警告,并打开浏览器输入网址 http://localhost:9000/,按下 F12,浏览器的控制台中同样出现了警

  • 手动修改 index.js 加上句尾分号,随即运行 npm start,观察控制台会发现警告消失了,并观察浏览器的控制台中的警告也消失了

如果同样想支持保存即自动格式化,只需在 webpack 配置文件的 EslintWebpackPlugin,中传入 { fix: true } 选项就可以了

NPM 插件 Perttier

Prettier 又是什么?和 ESLint 又有什么关系?

上述说到 ESLint 的作用是代码质量检测,Prettier 的作用则主要是代码格式化,那不是和 ESLint 冲突了吗?并不是,ESLint 只能格式化 js/ts 文件,而 Prettier 支持多种文件

  • JavaScript/TypeScript
  • CSS/Less/SCSS
  • HTML
  • JSON/YAML/GraphQL
  • Markdown
  • Vue/React/Angular

Prettier 自身的规范更倾向于个人/团队的代码风格的规范或统一,例如每行最大长度,单引号还是双引号,等号左右空格,使用 tab 还是 空格等等,将他与 ESLint 一起协同工作犹如如虎添翼

NPM 插件 eslint-plugin-prettier

eslint-plugin-prettier 是 一个 ESLint 插件, 由 Prettier 生态提供,用于报告错误给 ESLint

npm i prettier eslint-plugin-prettier eslint-config-prettier

eslint-config-prettier 的作用是使用 Prettier 默认推荐配置,并且关闭 eslint 自身的格式化功能,防止 PrettierESLint 的自动格式化的冲突

在项目根目录下添加 Prettier 配置文件 .prettierrc

{
  "singleQuote": true, // 强制单引号
  "semi": true // 强制句尾分号
}

修改 index.js

const hello = 'hello';
console.log(hello);

修改 .eslintrc.json

{
    "env": {
        "browser": true
    },
    "extends":["plugin:prettier/recommended"], // 使用 prettier 推荐配置
    "parserOptions": {
        "ecmaVersion": 12,
        "sourceType": "module"
    }
}

重新执行 npm start

  • 如果配置了 EslintWebpackPluginfix: true 选项,会发现双引号已变成单引号,并且自动加上了分号
  • 反之,则出现 eslint prettier 报错,错误的使用了双引号,并且未加句尾分号

ESlintPrettier 的合作关系: 由于 ESlint 自身自动格式化功能不够完善,Prettier 代替了 ESlint 格式化能力,利用了 ESlint 的检测和提示

VS Code ESLint/Prettier 插件

似乎还漏了什么,上面的代码的提示都是在项目编译时提示,好不容易写了一大串代码,运行时发现了 n 个语法警告和报错,顿时心态炸裂

VS Code ESLint 插件

回到 VS Code,按下 ctrl + shift + X(mac: command + shift + X),进入插件商店,安装 ESLint 插件,恢复 index.js 代码至以下

const hello = 'hello';

console.log(hello);

喜出望外,编辑器未雨绸缪的提示了报错与警告,不必再等到编译时提示了,在写代码的时候我们就可以发现错误了,这就是 VS Code ESLint 插件的能力。

VS Code ESLint 插件运行需要本地项目的 ESLint 相关插件和配置都存在(依赖,配置文件),但不一定要被编译。比如在 webpack 中可以不使用 eslint-loader 或者 eslint-webpack-plugin

VS Code Prettier 插件

在项目比较大文件比较多的时候,依赖 NPM Prettier 的自动格式化可能会导致保存后需要很久才能完成,因为它需要先编译一遍 javascript,这个时候 VS CodePrettier 插件的作用就体现出来了,使用上述中同样方法在插件商店安装 Prettier 插件

在项目的根目录下创建 .vscode 目录,进入目录创建 .settings.json 文件,添加以下代码

{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "[javascript]": {
    "editor.formatOnSave": false
  },
  "[javascriptreact]": {
    "editor.formatOnSave": false
  },
  "[typescript]": {
    "editor.formatOnSave": false
  },
  "[typescriptreact]": {
    "editor.formatOnSave": false
  }
}

配置大致描述为:启用 vscode eslint 插件的保存格式化,启用 vscode 自带的保存格式化,格式化插件为 prettier,并禁用 prettierJavaScript 相关文件的格式化。

意思就是:js 格式化只通过 eslint,其他类型文件使用 prettier,否则2种工具同时进行格式化可能会遇到冲突。

prettier 提供的 eslint 插件赋予了 eslint 更多的格式化功能,因此,我们现在可以将 eslint 作为我们的主要格式化手段了。

再次回到 index.js,保存,会发现代码已经被自动格式化了。此时修改 package.josnscripts 的 格式化 脚本为

"format": "eslint . --ext js,ts,tsx --fix && prettier --write *"

到了这个时候, 其实 VS Code 的 格式化已经和 NPM 的格式化冲突了,所以可以完全删除 eslint-webpack-plugin 插件了,解决冲突的同时,也大大的提高了代码的编译效率,,将检测与格式化的工作完全交给编辑器来做。这同样也是目前各大框架流行的做法,否则 webpack 中的 ESLint 也要完整解析一次代码,在项目比较大的时候特别影响编译速度,以及控制台带来的一连串警告和报错影响开发者的体验

对于需要个性化配置 prettier 的规则,可以在项目下新建 .prettierrcjson 文件,同样支持其他文件名称,具体见 prettier.io/docs/en/con…

ESLint配置

TypeScript

之前采用 tslint 作为 ts 代码的格式化工具,但是 tslint 有一个致命的问题就是只能支持 ts,而eslint 对于 tsjs 都支持,现在 tslint 已经不维护了并推荐使用 eslint,因此,我们可以全面拥抱 eslint

对于 ts, eslint 也不是添加支持,需要通过配置插件,目前全网都在使用的是 @typescript-eslint/eslint-plugin 这个插件,在 eslint 配置文件 extends 属性中,添加 plugin:@typescript-eslint/recommended 的推荐配置

另外,对于 ts 文件,还需要单独配置一下解释器,对 ts 代码进行解析

那为什么 JS 不需要呢?

eslint 默认带有一个解析器 parser ,但只转换 js,默认支持 ES5 的语法,可以通过制定 parserOptions 传递如下选项,我们文中开头提到过

"parserOptions": {
   "ecmaVersion": 6,  // 支持 es6 语法
   "sourceType": "module",
   "ecmaFeatures": {
      "jsx": true // 支持 jsx 语法,如果有需要
    }
}

eslint 会对我们的代码进行校验,而 parser 的作用是将我们写的代码转换为 ESTreeeslint 会对 ESTree 进行校验。

{
  "env": {
    "browser": true,
  },
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 6,
    "ecmaFeatures": {
      "jsx": true,
    },
  },
  "parser": "@typescript-eslint/parser",
  "extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
}

ESTree 只是一个 AST 的某一种规范,ESTree 本质上还是 AST

其他插件

Vue

如果你使用的是 Vue,需要使用 VUe 官方提供的 eslint-plugin-vue 插件,主要对 Vue 的单文件组件内的模板语法进行校验

React

如果你使用的是 React,则上述配置已经完全足够你使用了,当然,React 官方也抛出了一个 react-hooks 专门的插件 www.npmjs.com/package/esl… ,主要针对 react hooks 使用规范和依赖参数的校验。

总结

  1. NPM ESLint 插件 : 代码编译时时的语法检测
  2. NPM Prettier 插件: 代码编译时的自动格式化
  3. VS Code ESLint 插件: 编辑器对开发时的代码进行即时的语法检测和提示
  4. VS Code Prettier 插件: 编辑器对对开发时的代码进行即时的格式化

:::