200+收藏的Vue3规范,如何配置eslint、prettier、editorconfig

2,429 阅读10分钟

小插曲:前一篇《浅读Vue3代码10万行,总结出30个代码规范》,有同学提问:"你写的规范,有没有可以直接拿来用的配置?"。再加上最近新建项目,有同学也在提问如何为新项目配置代码规范。引申出来的思考:如何为项目配置代码规范,以及如何定制开发 my eslint插件?

要回答这两个问题,先得抓大放小,对代码规范配置有概览性认识,然后再从底层对eslint实现原理深入剖析。因此,我将分两篇来介绍如何为项目配置代码规范,以及开发一个自己的eslint插件。

本篇目的:当新建项目或者修改已有项目时,对涉及代码规范的eslint、prettier、editorconfig等工具的了解,能够达到"一览众山小"的程度。

代码规范往期介绍:

editorconfig、prettier、eslint的关系

当查看vue/core、element-plus、antd-design-vue等开源项目,必不可少的几个代码规范文件:

  • .eslintrc.js,或者.eslintrc.json,或者eslint.config.js
  • .eslintignore
  • .prettierrc
  • .prettierignore
  • .editorconfig

部分包含的规范文件:

  • .stylelintrc
  • .stylelintrc.json
  • commitlint.config.js

项目中经常看到好几个以rc作为后缀的文件,rc后缀有什么含义?rcrun commands的缩写,通俗点说,就是可执行命令的配置文件。例如,.eslintrc.js作为eslint工具的配置文件,.prettierrc作为prettier工具的配置文件。

eslint配置中,文件类型.js、.json、config.js无关紧要,其作用完全一样。

上述罗列的文件,stylelint用于css、scss、less等样式代码规范,commitlint用于提交代码时执行eslint检查。而eslint、prettier、editorconfig等经常在项目中看到的配置,各自的作用,以及三者之间有何关系?

工具作用范围配置文件特点使用方式
editorconfigIDE,如vscode、pycharm、github.editorconfig跨平台,适用于不同IDE,几乎支持所有类型文件vscode IDE默认支持editorconfig,无需插件,添加配置即可
prettierJSX、Vue、Angular、TS、CSS、HTML.prettierrc格式化js、css、html代码,保存、提交时自动格式化vscode插件:Prettier - Code formatter
eslintECMAScript/JavaScripteslintrc.js检查、修复js代码问题,保存时、提交时修复eslint npm plugin、vscode插件:eslint

上述表格对比了三者之间作用范围、自身特点。总结为:

  • editorconfig适用于IDE工具,支持所有类型文件格式化, vscode默认集成了editorconfig,只需添加.editorconfig文件即可生效;
  • prettier适用于js、css、html、markdown以及衍生语言代码格式化,除了配置,还需要安装vscode插件Prettier - Code formatter;
  • eslint适用于js以及衍生语言代码格式化和代码问题修复,除了配置,还需安装vscode插件eslint;

prettier

prettier有两种使用方式:命令模式、IDE模式。所谓命令模式即通过命令行方式执行代码格式化,而IDE模式即在IDE工具中通过快捷键或者保存时自动格式化。

命令模式

执行npm安装执行,安装prettier:

npm install --save-dev --save-exact prettier

在项目中添加.prettierrc、.prettierignore文件,以下配置来自于ant-design-vue的.prettierrc文件, 配置内容比较简单,针对每一个格式化规则设置相应参数。

{
  "singleQuote": true,
  "trailingComma": "all",
  "endOfLine": "lf",
  "printWidth": 100,
  "proseWrap": "never",
  "arrowParens": "avoid",
  "htmlWhitespaceSensitivity": "ignore",
  "overrides": [
    {
      "files": ".prettierrc",
      "options": {
        "parser": "json"
      }
    }
  ]
}

选项说明:

  • trailingComma:设置逗号结尾;
  • endOfLine:设置行以\n结尾;
  • printWidth:设置行最大字符长度为100;
  • proseWrap:设置markdown、text等文件换行行为;
  • arrowParens:设置箭头函数的括号是否保留,avoid表示省略括号,例如x => x
  • htmlWhitespaceSensitivity:用于指定HTML文件中空格处理的敏感度,确定Prettier如何解析和格式化HTML代码中的空格和换行。
  • overrides:为特定文件设置定制化格式,prettier默认会根据文件名自动推动使用哪种parser,其选项有babel、typescript、css、scss、json、html、markdown等。代码中为.prettierrc文件设置parser为json格式,因此prettier在处理.prettiercc文件时,会将其作为json文件处理。

prettier的命名模式可结合Git Hooks,在代码提交时自动执行文件格式化,可以在package.json文件中添加如下指令实现:

{ 
    "lint-staged": { 
        "**/*": "prettier --write --ignore-unknown" 
    }
}

IDE模式

prettier与IDE结合,可通过快捷键或者保存时自动格式化代码,前提是需要为IDE安装prettier插件。VSCode可在插件市场搜索Prettier - Code formatter

设置保存时自动格式化需要在项目的.vscode/settings.json中添加配置项:

{  
    "editor.defaultFormatter": "esbenp.prettier-vscode",  
    "[javascript]": {  
        "editor.defaultFormatter": "esbenp.prettier-vscode"  
    }  
}

通过快捷键格式化代码,执行快捷键CMD/CTRL + Shift + P,如果有选中文件中的部分代码,则仅格式化选中部分。

prettier、eslint、stylelint格式化冲突

eslint、stylelint不但提供了代码检查功能,还提供了代码风格检查,而代码风格检查部分与prettier的配置项可能冲突,如何解决冲突问题?

专业的事交给专业的人做,最好是prettier负责代码风格格式化,而eslint、stylelint仅负责代码质量规则

prettier提供了eslint-plugin-prettier插件,用于关闭eslint中代码风格格式化,因此代码风格将按照prettier设置格式化。同理,prettier提供stylelint-prettier插件,用于关闭css、html等风格检查。

如何在eslint中配置prettier插件? 可在.eslintrc.*文件中添加如下配置:

{
  "extends": [
    "some-other-config-you-use",
    "prettier"
  ]
}

prettier与editorconfig冲突

问题:当prettier、editorconfig同时设置行字符串最大长度,那代码格式化按哪一个配置执行?

// prettier配置
"printWidth": 100,

// editorconfig配置
max_line_length = 80

prettier对editorconfig也提供了适配能力,当项目根目录配置有.editorconfig,prettier会自动将.editorconfig中配置的属性映射为prettier属性,如果prettier提供有相同属性则优先使用prettier配置。

下面是prettier官方给的一段.editorconfig配置chartsetinsert_final_newline配置prettier无映射,则直接保留;而end_of_lineindent_size等配置prettier有映射,则将被映射至prettier属性。

# Stop the editor from looking for .editorconfig files in the parent directories # root = true 
[*] # Non-configurable Prettier behaviors 
charset = utf-8 
insert_final_newline = true 

# Caveat: Prettier won’t trim trailing whitespace inside template strings, but your editor might. 
# trim_trailing_whitespace = true 

# Configurable Prettier behaviors 
# (change these if your Prettier config differs) 
end_of_line = lf 
indent_style = space 
indent_size = 2 
max_line_length = 80

eslint

一个新项目增加eslint配置,可通过官方提供的指令将其引入到项目中:

npm init @eslint/config@latest

执行后项目根目录会添加eslint.config.js文件,内容如下:

import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginVue from "eslint-plugin-vue";


export default [
  {files: ["**/*.{js,mjs,cjs,ts,vue}"]},
  {languageOptions: { globals: globals.browser }},
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  ...pluginVue.configs["flat/essential"],
  {files: ["**/*.vue"], languageOptions: {parserOptions: {parser: tseslint.parser}}},
];

配置为数组结构,每一项为Configuration Objects类型,object包含的属性有:name、files、rules、ignores、languageOptions、linterOptions、processor、plugins、settings

数组结构中,后一项配置继承或覆盖前一项配置,类似于merge并覆盖的效果,例如第一项的files适用于后续配置项。

Configuration Object属性

name

检查项标识,如下图所示,@typescript-eslint/no-unused-vars表示插件@typescript-eslint下名称为no-unused-vars的检查项错误。

image.png

files

规则支持的文件类型,["**/*.{js,mjs,cjs,ts,vue}"]表示适用于js、cjs、ts、vue文件;

ignores

和files相反,表示哪些文件排除检查,也可以在.eslintignore文件中添加排除项;

rules

rules添加规则配置项,semi为配置规则名称,value error为规则参数,其值包含off、error、warn。如下配置表示需要提供分号,否则报eslint错误。

{
    rules: {
        semi: "error"
    }
}

可以为规则提供额外的参数,此时,value为数组类型。例如,如果semi规则不允许分号,可配置为["error", "never"],其中never表示不允许分号结束,否则报错。

{
    rules: {
        semi: ["error", "never"]
    }
}

image.png

languageOptions

languageOptions配置指定如何检查错误,可理解为错误检查的驱动器,其核心为parser选项

思考问题:eslint是如何检查类似于no-unused-vars这样的错误? 要识别代码错误,需要先将代码字符串转换为语法树AST,再通过语法树来判断代码中的变量是否有被使用。而语法树的生成正是通过parser选项实现。

parse方法转换js代码生成ESTree语法树,而parseForESLint用于Vue、TS等语法树生成。parser类型定义如下:

type ParserModule = {
    /**
     * Parses the given text into an ESTree AST
     */
    parse(text: string, options?: ParserOptions): TSESTree.Program;
} | {
    /**
     * Parses the given text into an AST
     */
    parseForESLint(text: string, options?: ParserOptions): ParseResult;
};

目前常用的parser模块有:

  • espree
  • Esprima
  • @babel/eslint-parser
  • @typescript-eslint/parser

espree为默认parser,支持将js转换为抽象语法树,执行如下代码:

import * as espree from "espree"

const ast = espree.parse('let foo = "bar"', { ecmaVersion: 6 })
console.log(ast)

运行结果如中,type表示类型, 如变量定义VariableDeclarator;kind标识变量类型,如let;id为变量名标识,其名称为foo

image.png

再回顾eslint.config.js文件中代码片段, 其中有:

import tseslint from "typescript-eslint"

{files: ["**/*.vue"], languageOptions: {parserOptions: {parser: tseslint.parser}}}

这里的parser值为tseslint.parser,使用的即是typescript-eslint提供的parser方法,实现了parseForESLint方法,用于处理typescript类型代码。代码定义如下:

function parse(
  code: ts.SourceFile | string,
  options?: ParserOptions,
): ParseForESLintResult['ast'] {
  return parseForESLint(code, options).ast;
}

function parseForESLint(
  code: ts.SourceFile | string,
  parserOptions?: ParserOptions | null,
): ParseForESLintResult {
    ...
}

export { parse, parseForESLint, ParserOptions };

processor

由于需要代码检测的文件类型很多,如.js、.ts、.vue、.tsx、markdown等,processor的作用是提取不同类型文件中代码模块,并将其转换为Lint能识别的Message格式。例如插件@eslint/markdown,支持从markdown文件中提取js代码模块。一个processor的类型格式如下:

 interface LooseProcessorModule {
    /**
     * Information about the processor to uniquely identify it when serializing.
     */
    meta?: {
        [K in keyof ProcessorMeta]?: ProcessorMeta[K] | undefined;
    };
    /**
     * The function to extract code blocks.
     */
    preprocess?: (text: string, filename: string) => any;
    /**
     * The function to merge messages.
     */
    postprocess?: (messagesList: any, filename: string) => any;
    /**
     * If `true` then it means the processor supports autofix.
     */
    supportsAutofix?: boolean | undefined;
}

如何使用三方配置

要使用三方配置,有两种方式引入:直接引入三方的rules配置,或者通过插件形式引入部分配置。

引入三方的rules配置

回顾eslint.config.js代码片段,两段recommended,一段flat/essential,第一项没有解构,第二、三项有解构。

pluginJs.configs.recommended,
...tseslint.configs.recommended,
...pluginVue.configs["flat/essential"],

pluginJs.configs.recommended为eslint默认推荐的rules配置, 其类型定义如下:

readonly configs: {
    readonly recommended: { readonly rules: Readonly<Linter.RulesRecord> };
};

由于其本身为Record类型,而非数组类型,因此不需要解构,部分源代码如下:

module.exports = Object.freeze({
    rules: Object.freeze({
        "constructor-super": "error",
        "for-direction": "error",
        "getter-return": "error",
        ...
    })
});

tseslint.configs.recommendedpluginVue.configs["flat/essential"]的类型为TSESLint.FlatConfig.ConfigArray数组,因此需要解构。查看tseslint.configs.recommended源代码, 返回结果为数组类型,而每一项则为 Configuration Object类型。

/**
 * Recommended rules for code correctness that you can drop in without additional configuration.
 * @see {@link https://typescript-eslint.io/users/configs#recommended}
 */
export default (
  plugin: FlatConfig.Plugin,
  parser: FlatConfig.Parser,
): FlatConfig.ConfigArray => [
  baseConfig(plugin, parser),
  eslintRecommendedConfig(plugin, parser),
  {
    name: 'typescript-eslint/recommended',
    rules: {
      '@typescript-eslint/ban-ts-comment': 'error',
      'no-array-constructor': 'off',
      ...
    },
  },
];

Plugin形式引入

ESLint支持插件形式扩展规则,命名形式为eslint-plugin-XXX。在eslint.config.js中可通过配置引入三方插件,并支持部分规则引入。 假设现在有example插件,我们想引入其下rule名称为rule1的规则,可通过如下配置引入:

// eslint.config.js
import example from "eslint-plugin-example";

export default [
    {
        plugins: {
            example
        },
        rules: {
            "example/rule1": "warn"
        }
    }
];

其中plugins选项配置需要引入的插件,而rules选项可通过插件名/rule名称形式引入插件下的具体规则。

总结

相信通过对editorconfig、prettier、eslint的介绍,当面临一个新项目,需要添加代码规范配置时,我们能够很从容的完成这三者的相关配置,并且知道其核心配置的作用。

editorconfig、prettier、eslint没有谁最重要的说法,而是相辅相成的关系。editorconfig通用型更强,能对所有文件设置基础的代码风格。prettier专注于前端代码风格,但也可以通过plugin方式扩展其他语言,例如支持java的prettier-plugin-java、插件、支持SQL的prettier-plugin-sql插件。eslint侧重于代码质量检查,也是我们在项目中使用最频繁的工具。

开发人员不需要过于关注各个工具之间的规则冲突,prettier已为开发人员提供了完善的解决方案,自动映射editorconfig配置,通过eslint-plugin-prettier、stylelint-prettier插件解决javascript、html、css风格冲突。

我是前端下饭菜,原创不易,各位看官动动手,帮忙关注、点赞、收藏、评轮!