前端项目中代码格式化配置:eslint + prettier + stylelint + pre-commit

前端项目代码格式化配置:eslint + prettier + stylelint + pre-commit

目标:

  1. eslint 进行代码质量检测
  2. 统一编码风格,代码自动格式化
  3. css 规范
  4. 提交代码自动检测和修复

eslint

ESLint 是一个 Javascript Linter,ESLint 主要解决的其实是代码质量问题,因为代码质量出问题那就意味着有很大几率会出现 BUG。ESLint 是完全插件化的,每一个规则都是一个插件并且你可以在运行时添加更多的规则。
使用 eslint 好处:

  • 避免代码错误
  • 写出最佳实践的代码
  • 规范变量使用方式
  • 规范代码格式
  • 更好的使用新的语法

eslint 安装

eslint 这里使用 ts 相关版本,安装相应的 eslint 包有:

  • eslint
  • @typescript-eslint/parser(用来解析 ts 语句。eslint 本身不支持 ts 语法)
  • @typescript-eslint/eslint-plugin (已不维护之前的 tslint 改用这个,这是针对 ts 制定了一系列的 eslint 规则)
  • eslint-plugin-react
  • eslint-plugin-import

运行安装:

npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react -D

修改配置

我们把所有关于 eslint 的配置都放到 eslintrc.js 文件中,删除 package.json 里面关于 eslint 的配置:

"eslintConfig": {
  "extends": [
    "react-app",
    "react-app/jest"
  ]
},

新建 .eslintrc.js

module.exports = {
  extends: ['react-app', 'plugin:prettier/recommended'],
  rules: {
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-console': 0,
    'prettier/prettier': 2,
  },
};

配置项

module.exports = { 
  // 默认情况下,ESLint会在所有父级组件中寻找配置文件,一直到根目录。ESLint一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。
  root: true,
  // 指定环境
  env: {
    browser: true, // 预定义的全局变量
    es6: true, // 启用 ES6 语法支持以及新的 ES6 全局变量或类型
  },
  // 扩展配置文件,主要用来使用第三方的eslint的完整配置,一般输出的是整个eslint配置,也可以是别的形式
  extends: [
    'plugin:vue/essential',
    'eslint:recommended',

    // 'prettier', // Prettier 是一个代码格式化插件。它并不关心你的语法是否正确,只关心你的代码格式,比如是否使用单引号,语句结尾是否使用分号等等。但是有部分规则和Eslint的冲突。
    // 'airbnb-base', // 包含 ECMAScript 6 + 代码规范。 Airbnb 是其中一个最流行的 JavaScript 代码规范,它差不多包含所有 JavaScript 的角度。校验比较严格。
    // 'eslint:recommended', // eslint官方扩展
    // 'plugin:react/recommended', // 插件类型的扩展,也可以直接在 plugins 属性中进行设置
    // 'eslint-config-standard', // 第三方扩展,eslint-config-*

    //避免与 prettier 冲突
    'plugin:prettier/recommended',

    // @vue/cli集成ESLint 使用
    /**
     'plugin:vue/essential',
      【ESLint + Airbnb】
      '@vue/airbnb',
      【ESLint + Standard】
      '@vue/standard'
      */
    // 【ESLint + Prettier】
    // '@vue/prettier',
  ],
  // 配置插件,使用第三方的eslint的rules,命名格式eslint-plugin-*,插件一般输出的是规则
  plugins: [],
  
  parserOptions: {
    // 对Babel解析器的包装使其与 ESLint 兼容。
    parser: 'babel-eslint'
  },
  // 启用的规则及其各自的错误级别
  rulse: {},
  // 指定全局变量
  // 1. 将每个全局变量名称设置为等于以true允许覆盖变量或false禁止覆盖
  // 2. 还支持在单独的js文件中通过注释配置全局变量 `/* global var1:false, var2:false */`
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
    ENV: true,
  },
  // 禁用一组文件的配置文件中的规则
  overrides: [
    {
      files: ['*-test.js', '*.spec.js'],
      rules: {
          'no-unused-expressions': 'off',
      },
    },
  ],
}

1.parser 解析器及解析参数
parser 指的是将 .js、.ts 文件处理为 AST 使用的解析器。ESLint 附带了一个内置的解析器(espree),因此如果你只编写标准的 JavaScript,就不需要自定义解析器了
如果我们想支持非标准 JavaScript 语法,我们需要做的就是为 ESLint 提供一个可供使用的替代解析器。比如基于 vue 工程需要用到能解析 vue 语法解析器;再比如使用到 TypeScript 的工程,需要配置能解析 TypeScript 的解析器

这里可以类比 webpack,自身只能处理 js、及 json 文件,css、html 等文件需要通过自定义添加 loader 处理来理解

常用解析器:

  • @babel/eslint-parser:一个对Babel解析器的包装,使其能够与 ESLint 兼容
  • @typescript-eslint/parser:TSLint 被弃用后,将 TypeScript 转换成与 estree 兼容的形式,以便在 ESLint 中使用
{
  "parserOptions": {
    parser: '@babel/eslint-parser',
    // 使用的 ECMAScript 版本
    "ecmaVersion": 6,
    // 设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
    "sourceType": "module",
  }
}

2.rules 规则
启用的规则及其各自的错误级别,ESLint 附带有大量的规则,要改变一个规则设置,你必须将规则 ID 设置为下列值之一:

  • “off” 或 0 - 关闭规则
  • “warn” 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • “error” 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
"rules": {
  "eqeqeq": "off", // 要求使用 `===` 和 `!==`
  "curly": "error", // 强制所有控制语句使用一致的括号风格
  "quotes": ["error", "double"] // 引号
}

3.plugins 插件
ESLint 虽然可以定义很多的 rules,以及通过 extends 来引入更多的规则,但是说到底只是检查 JS 语法。如果需要检查 Vue 中的 template 或者 React 中的 jsx,就束手无策了。所以引入插件的目的就是为了增强 ESLint 的检查能力和范围。

{
  "plugins": [
    "vue", // 等价于"eslint-plugin-vue"
    "@typescript-eslint" // 等价于@typescript-eslint/eslint-plugin
  ]
}

4.extends 配置扩展
实际项目中配置 rules 的时候,不可能团队一条一条的去商议配置,太费精力了。通常的做法是使用业内大家普通使用的、遵循的编码规范;然后通过 extends 去引入这些规范。extends 配置的时候接受字符串或者数组:

  • 指定配置的字符串(配置文件的路径、可共享配置的名称、eslint:recommended 或 eslint:all)
  • 字符串数组:每个配置继承它前面的配置

extends 可以理解为 rules 的快捷配置字段,一般先配置 extends 为业内通用规范,且数组时后面的会覆盖前面的,并自定义配置 rules 覆盖 extends 中不满足你想要规范的规则

{
  "extends": [
    "eslint:recommended",
    "plugin:vue/essential", // 实际npm包插件eslint-config-vue/essential [插件名:配置名]
    "airbnb-base", // 实际npm包插件eslint-config-vue/airbnb-base
    "@vue/prettier",
    "./node_modules/coding-standard/.eslintrc-es6"
  ]
}

从上面的配置,可以知道 extends 支持的配置类型可以是以下几种

  • eslint 开头的:是 ESLint 官方的扩展;常见配置 eslint:recommended、eslint:all
  • plugin 开头的:是插件类型扩展,可以省略包名的前缀 eslint-plugin-,比如 plugin:vue/essential,实际上使用的是 eslint-config-vue 插件,而 essential 是配置名,可见插件配置说明,如下图,包 eslint-config-vue 中定义了配置参数 essential
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKupHq2f-1672294658557)(./src/imgs/plugin.png)]
  • eslint-config 开头的:来自 npm 包,使用时可以省略前缀 eslint-config-,比如上面的直接写成 airbnb-base;
  • @开头的:扩展和 eslint-config 一样,只是在 npm 包上面加了一层作用域 scope;
  • 一个执行配置文件的相对路径或绝对路径;

常用 extends 配置:

  • eslint:recommended:ESLint 内置的推荐规则,属性启用一系列核心规则,这些规则报告一些常见问题,在 规则 页面中被标记为✅
  • eslint:all:ESLint 内置的所有规则;
  • eslint-config-airbnb-base:airbnb 的 JS 规范;
  • eslint-config-standard:standard 的 JS 规范;

校验文件

完成配置就可以运行 eslint 对 js 文件进行校验了

// 校验 a.js 和 b.js 
npx eslint a.js b.js 
// 校验 src 和 scripts 目录 
npx eslint src scripts

通常 ESLint 只能校验 js 文件。比如需要校验 .vue 文件,只配置 vue 插件和 vue-eslint-parser 解析器是不够的,还需要让 ESLint 在查找文件的时候找到 .vue 文件。ESLint 提供了 --ext 来指定具体需要校验的文件

npx eslint --ext .js,.vue src

ESLint提供了–fix 来根据规则进行修复
在 pakage.json 中配置

"scripts": { 
  "lint": "npx eslint --ext .js,.vue src", 
  "lint:fix": "npx eslint --fix --ext .js,.vue src", 
}

对于一些公共的 JS、测试脚本或者是特定目录下的文件习惯上是不需要校验的,因此可以在项目根目录通过创建一个 .eslintignore 文件来配置,告诉 ESLint 校验的时候忽略它们:

# Ignore artifacts:
build
config
node_modules
public
README.md
package.lock

在 vscode 中配置自动检测和修复代码

前面做的配置,都需要执行命令才能进行检查和修复代码,还是挺不方便的,如果我希望编辑完或者保存时去检查代码该如何做呢?可以直接在 IDE 里安装 ESLint 插件
在全局配置文件中配置,或者工作区新建 .vscode/settings.json 文件添加保存是进行 eslint 校验和修复

{
  "eslint.alwaysShowStatus": true,  // 总是在 VSCode 显示 ESLint 的状态
  "eslint.quiet": true,             // 忽略 warning 的错误
  "editor.codeActionsOnSave": {     // 保存时使用 ESLint 修复可修复错误
    "source.fixAll": true,
    "source.fixAll.eslint": true
  }	
}

在某些场景下禁用 eslint 校验

当你引入改写引入的组件时,通常需要关闭ESLint检测,以下是集中关闭方式
(1)关闭所有规则/* eslint-disable */

/* eslint-disable */
...
/* eslint-enable */

(2)关闭某一行的所有规则// eslint-disable-line

...; // eslint-disable-line

// eslint-disable-next-line
...;

(3)在某一行关闭指定的规则 // eslint-disable-line no-alert

...; // eslint-disable-line no-alert

// eslint-disable-next-line no-alert
...;

prettier

上面的 eslint 我们可以采用比较出名的 airbnb、 standart 规范来格式化代码风格。ESLint 主要是解决代码质量问题,对于代码风格问题并没有太多的约束,而且就算是 Airbnb 也并不是覆盖方方面面的风格,而且也不是所有人都能接受的。Prettier 是代码格式化工具,它是 opinionated 的。采用 Prettier 的最大原因是为了停止所有关于代码风格的争论,它认为使用哪种代码风格不重要,重要的是用没用。所以 Prettier 提倡先提高代码的可读性和可维护性,至于具体使用什么风格它给我们定,这也是 opinionated 的意义。所以最好的处理方式是 Prettier 只负责代码格式的检测并修复,ESLint 只负责代码质量的检测,各自负责自己专项的领域就好了。
Prettier 做的事情是它不管你写的代码是什么样子的,它会去掉代码里的所有样式风格,然后用统一固定的格式重新输出。它通常是考虑行长(line length)来获取代码并重新输出。统一的编码风格能很好的保证代码的可读性。

prettier 安装

  • prettier
  • eslint-config-prettier (关闭所有和 prettier 冲突的 eslint 规则prettier 的规则时没有办法关闭的)
  • eslint-plugin-prettier (将 prettier 的 rules 以插件的形式加入到 ESlint里面,配合 rules “prettier/prettier”: “error” 实现 Eslint 提醒)

安装:

npm install prettier eslint-config-prettier eslint-plugin-prettier -D

新建 .prettierrc.js 放入自己定义的规则

module.exports = {
  singleQuote: true, // 单引号
  tabWidth: 2,
  quoteProps: 'as-needed', // 对象中属性加引号
  bracketSpacing: true, // 对象字面量括号中的空格
  trailingComma: 'all', // 尾逗号
  printWidth: 80,
  useTabs: false,
  endOfLine: 'lf',
  bracketSameLine: true,
  jsxSingleQuote: true,
  singleQuote: true,
  semi: true,
  arrowParens: 'always',
  jsxBracketSameLine: true,
};

需要在.eslintrs.js 的配置中将
'plugin:prettier/recommended'
放到extends最后,这个配置包含两个内容:

  • 使用 eslint-config-prettier 来关掉eslint 中所有与 prettier 冲突的配置。
  • 使用 eslint-plugin-prettier 将 prettier 的 rules 以插件的形式加入到ESlint里面。
    相当于:
  1. 在 extends 中使用 eslint-config-pretter 配置关掉与 eslint 规则的冲突

    // .eslintrc    
    {      
      "extends": ["prettier"] // prettier 一定要是最后一个,才能确保覆盖    
    }
    
  2. (可选,推荐) 然后再启用 eslint-plugin-prettier ,将 prettier 的 rules 以插件的形式加入到 ESLint 里面。这里插一句,为什么"可选" ?当你使用 Prettier + ESLint 的时候,其实格式问题两个都有参与,disable ESLint 之后,其实格式的问题已经全部由 prettier 接手了。那我们为什么还要这个 plugin?其实是因为我们期望报错的来源依旧是 ESLint ,使用这个,相当于把 Prettier 推荐的格式问题的配置以 ESLint rules 的方式写入,这样相当于可以统一代码问题的来源。

    // .eslintrc    
    {      
      "plugins": ["prettier"],      
      "rules": {        
        "prettier/prettier": "error"      
      }    
    }
    

    将上面两个步骤和在一起就是下面的配置,也是官方的推荐配置

    {
      "extends": ["plugin:prettier/recommended"]
    }
    

使用 stylelint 进行 css 格式化

  • stylelint (主依赖必须)
  • stylelint-config-standard (官网提供的 css 标准)
  • stylelint-scss (校验 scss 语法的插件,例如@extend 等)
  • stylelint-config-standard-scss (针对 scss 的标准可共享配置)
  • stylelint-order (用于规范样式属性写作顺序的插件)
  • stylelint-config-rational-order (针对 css 属性排序的共享规则配置,避免长串 css 属性顺序规则书写)
  • stylelint-prettier (基于 prettier 代码风格的 stylelint 规则)
  • stylelint-config-prettier (禁用所有与格式相关的 Stylelint 规则,解决 prettier 与 stylelint 规则冲突,确保将其放在 extends 队列最后,这样它将覆盖其他配置)

安装依赖:

npm install stylelint stylelint-config-standard stylelint-config-standard-scss stylelint-order stylelint-config-rational-order stylelint-prettier stylelint-config-prettier stylelint-scss -D

根目录新建.stylelintrc.js 文件

module.exports = {
  extends: [
    'stylelint-config-standard',
    'stylelint-config-standard-scss',
    'stylelint-config-rational-order',
    'stylelint-prettier/recommended',
  ],
  rules: {
    indentation: 2, // 用来定义缩进的单位
    'no-missing-end-of-source-newline': null, // 为true的时候,每一段css或者scss在最后必须要以新的空行为结尾
    'max-nesting-depth': 10, // 限制scss中&字符嵌套深度
    'selector-max-compound-selectors': 10, // 限制选择器深度
    'at-rule-no-unknown': null, // 为true sass有些语法会报错
    'scss/at-rule-no-unknown': true, //如果@extend错写成@exend,则stylelint会因为此规则而报错。
  },
  plugins: [
    'stylelint-scss',
    'stylelint-order',
    'stylelint-config-rational-order/plugin',
  ],
};

配置项

extends

extends 定义了一个数组,该配置项允许我们 extend 一个已存在的配置文件(无论是你自己的还是第三方的配置)。当一个配置继承了另一个配置,它将会添加自己的属性并覆盖原有的属性。
如果extends 中包含多个配置项,那么数组中的每一项都优先于前一项,也就是说第二项会覆盖第一项,第三项会覆盖第一项和第二项,最后一项将覆盖其它所有项。跟eslint的配置文件 .eslintrc.js 中的 extends 属性用途一样

plugins

plugins 定义了一个数组,该配置项允许我们使用第三方插件。一旦声明了插件,在rules中需要为插件的规则添加选项,就像其他标准的规则一样。你需要查看插件的文档去了解规则的名称。

rules

rules 就是用来定义 css 的规则,因为优先级比 extends 高,所以可以用来覆盖extends 中包含在集合里的规则。有时候想要禁用某些规则,只需要在rules里添加上对应的规则,并将值设置为 null,如:

 "selector-class-pattern": null

根目录新建 .stylelintignore 文件(忽略的文件)

node_modules
public
dist
es
lib
*.d.ts

vscode 中保存自动格式化

在 vscode 中安装插件 stylelint, 并在 settings.json 添加规则,(如果是仅在工作区生效,可在根目录下建立.vscode文件夹,并新建settings.json)

{ 
  // 关闭编辑器内置样式检查(避免与stylelint冲突)
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,  
  // 配置stylelint检查的文件的范围
  "stylelint.validate": ["css", "less", "postcss", "scss", "sass", "stylus", "vue"],
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.fixAll.stylelint": true,
  },
}  

上面的配置添加后,每次保存的时候会自动进行格式化,(比如删除空行,重新排序需要再次 ctrl+s)

package.json 中添加 stylelint 相应的自动修复

"scripts": {
  "lint:style":"stylelint src\"/**/*.+(css|scss|less|stylus|sass|postcss)\" --fix",
}

并行运行命令

主要是方便我们写前端工程化的时候,我们可以同时启动多个命令用,如:我运行 lint 时,既要用 eslint、prettier 格式化和修复逻辑代码,又要使用 stylelint 格式化 scss 文件代码
工具: concurrently

安装依赖

npm install concurrently -D

同时执行多个命令:

"lint:format": "concurrently \"npm run lint\" \"npm run format\""

当我们需要执行 eslint, prettier,和 stylelint 时 可以只运行一条 npm run lint:format

"lint:prettier": "prettier --write src\"/**/*.+(js|ts|tsx|jsx|json|md|json)\"",
"lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx src",
"lint:style":"stylelint src\"/**/*.+(css|scss|less|stylus|sass|postcss)\" --fix",
"lint:format": "concurrently \"npm run lint:prettier\" \"npm run lint:style\" \"npm run lint:fix\""

pre-commit

  • 通过以上工具自动修复能够修复的错误及代码的格式化,但是在实际开发的时候难免会遇到无法 fix 的错误,可能开发人员也忘记修改,如果这个时候把代码提交到远程仓库,那就把糟糕的代码给提交上去了
  • 使用命令修复的时候,不小心修改了他人代码,很容易造成冲突。如果一个项目以前没有用过 linter,如何最方便地开始使用并且尽量不改动之前的代码?

解决以上问题的一个方案就是,在提交代码的时候,自动修复格式问题,并且只修复自己改动的代码,可以通过配置 git hooks 的 pre-commit 钩子来实现这个目的。主要是利用了 husky 和 lint-staged 这 2 个包。

配置husky

我们需要引入强制的手段来保证提交到 git 仓库的代码是符合我们的要求的。husky 是一个用来管理 git hook 的工具,git hook 即在我们使用 git 提交代码的过程中会触发的钩子。简单来说,我们需要用 husky 安装 pre-commit 钩子,这样就可以在 git commit 之前运行脚本来检测提交的代码是否规范并且格式化不规范的代码。

安装

安装 husky

npm install husky -D

安装钩子,该命令会创建 .husky/ 目录并指定该目录为 git hooks 所在的目录

npx husky install

为了让拉取项目的其他同事一定会这样执行一次。这时候我们需要用 npm scripts的钩子:prepare,prepare 脚本会在 npm install 之后自动执行

"scripts": {
  "prepare": "husky install",
}

接下来我们就为 git 添加一个 pre-commit 钩子

npx husky add .husky/pre-commit "npm run lint:style && npm run lint:prettier && npm run lint"
  • npx husky —— 运行 husky
  • add .husky/pre-commit —— 添加一个 pre-commit 钩子
  • npm run format && npm run lint:style && npm run lint —— 先 format 代码然后用 eslint 检查

这样上面的代码在提交前会将我们整个项目的全部代码进行格式化和检查,通过分析我们发现,commit 前应该只需 lint 暂存区的代码即可。需要用到 lint-staged,lint-staged 中 staged 指的是 git 的提交区。

当我们运行上面命令的时候可能会报错:

Usage
husky install [dir] (default: .husky)
husky uninstall
husky set|add  [cmd]

原因:应该是语法太多,导致他好像有点不能识别这么多的语法,一步步拆开就行了

解决:

1.先在 .husky 目录下创建一个 pre-commit 文件
npx husky add .husky/pre-commit

2.然后打开 pre-commit 文件,将我们要运行的命令(npm run lint…)替换里面的 undefined 即可

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint:style & npm run lint:prettier & npm run lint:fix

### 配置 lint-staged
如果发现不符合代码规范的文件则直接退出 commit,且会自动将 prettier 格式化后的代码添加到此次 commit 中
安装 lint-staged

npm install lint-staged -D


在packge.json 中增加配置

```json
"lint-staged": {
  "*.{css,less,scss,stylus,sass,postcss}": [
    "stylelint --fix", "stylelint --fix", "prettier --write"
  ],
  "*.{html,ts,js,json,jsx,tsx}": [
    "prettier --write",
    "eslint"
  ]
}

上面的第二个正则配置是指对 .html、.js、.jsx、.ts、.tsx 文件进行 prettier 格式化,然后执行 eslint
在 .husky/pre-commit 中添加 lint-staged 的调用,修改 ./husky/precommit 内容,将之前运行 husky add 生成的命令注释或者删除即可,我们现在只运行 lint-stage 相关的命令

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# npm run lint:style & npm run lint:prettier & npm run lint:fix

npx lint-staged

现在在 pre-commit 阶段就只会去对 stage 的文件运行 packge.json lint-stage 中的格式化命令

解决在某些 git 客户端我们用 husky 加的 precommit 不生效的问题

添加一个 bash 命令来将安装的钩子和 husky 文件复制到标准钩子文件夹,我们就可以解决 GitKraken 的问题

cp -a .husky/. .git/hooks/

我们可以将 prepare 进行修改:

"scripts": {
  "prepare": "husky install && cp -a .husky/. .git/hooks/",
}

由于运行 npm install 是所有开发人员在克隆存储库时都会开始的操作,因此我们可以确保我们的基础配置,并允许开发人员想要使用的任何 git 客户端 详细参考

总结

  1. 通过 eslint 完成对规则的限制
  2. 通过 prettier 完成对格式化定义,以及使用 eslint-config-prettier 抹平与 eslint 自带格式化的冲突问题
  3. 通过 stylelint 完成对 css 的检查和格式化,以及使用 stylelint-config-prettier 抹平与 prettier 格式化的冲突问题
  4. 通过 husky 添加 pre-commit 钩子,这里还用的 scripts 的一个钩子 —— prepare 用来执行 husky install
  5. 通过 lint-staged 完成只对暂存区代码的校验和格式化工作
  6. 搭配 VSCode 的插件 StyleLint 和 ESLint 以及 Prettier - Code formatter 使用加强提示和自动格式化功能

你可能感兴趣的:(随笔,前端,javascript)