手写 Element Plus : Element Plus 的 ESLint 、 Prettier 配置

一、前言

Element Plus 组件非常庞大,多人开发是必要的,但多人开发存在开发人员的代码风格不一致,需要规范代码才能提交。<font style="color:#117CEE;">为了统一代码风格</font>,我们可以使用ESLint 代码校验工具,通过配置 ESLint 相关配置来对代码进行检测,提醒我们的按照符合校验的代码风格进行编码。

本章节将会详细介绍 ESLint 在 Element Plus 中的配置,以及详细概括 ESLint 的基本原理、配置文件中的配置项和 Prettier 的基本使用。

二、ESLint 的工作原理

一)、寻找配置文件

ESLint 会在项目的根目录或者指定路径下寻找配置文件,配置文件可以支持多种格式 。

  • JavaScript 文件:( .eslintrc.js 或 eslint.config.js )
  • JSON 文件:( .eslintrc.json )
  • YAML 文件:( .eslintrc.yml 或 .eslintrc.yaml )

ESLint 会先寻找 .eslintrc.* 文件,如果这些文件都不存在,会去查找 package.json 中的 eslintConfig 字段。ESLint 会从根目录或指定路径,向上递归查找配置文件,如果在<font style="color:#117CEE;">某个配置文件中设置了 </font>**<font style="color:#117CEE;">root:true</font>**<font style="color:#117CEE;">,则会终止向上查找,表示这个配置文件是根配置文件。</font>

二)、解析源码生成 AST 、分析 rules 配置项原理

1、解析源码生成 AST

ESLint 是一个基于抽象语法树( AST )静态代码分析工具、它通过使用**<font style="color:#117CEE;">解析器解析源码,转换为 AST</font>**** **, ESLint 中默认的解析器( parser )是 Espree,用来解析 ECMAScript 规范中的 JavaScript 代码。如果想使用其他的解析器,可以直接指定 parser 配置项。比如 parser:@typescript-eslint/parser

2、分析 rules 配置项原理

获取到 AST 后, ESLint 会遍历 AST ,在遍历的过程中,ESLint 会应用用户在配置项配置的 rules ,每个规则都注册了特定的节点类型,当遍历到与规则相关的节点时,ESLint 会调用规则的处理函数进行检查。rules 的执行原理分为以下步骤。

读取配置、加载规则、解析规则配置、遍历 AST、执行规则处理函数、报告处理、自动修复(可选)、输出结果。

1)、规则源码的代码结构

每个 ESLint 规则源码通常有以下结构

module.exports = {
  meta: {
    type: "problem", // 规则的类型,可以是 "problem", "suggestion", "layout"
    docs: {
      description: "disallow the use of 'var'",
      category: "Best Practices",
      recommended: true
    },
    fixable: "code", // 是否支持自动修复
    schema: [] // 规则的配置选项,通常是一个 JSON schema
  },
  create(context) {
    return {
      VariableDeclaration(node) {
        if (node.kind === "var") {
          context.report({
            node,
            message: "Unexpected var, use let or const instead.",
            fix(fixer) {
              return fixer.replaceText(node, "let"); // 自动修复代码
            }
          });
        }
      }
    };
  }
};

2)、加载和解析规则

ESLint 读取配置文件会读取 rules 配置项,配置项指定了要启用的规则,ESLint 通过规则名称在 <font style="color:#117CEE;">ESLint 内置的规则集</font>或者<font style="color:#117CEE;">自定义的规则</font>中找到对应的规则模块,如果这个规则被启用,会为这个规则 其传入 context 对象。context 提供了规则执行过程中所需的上下文信息和工具(如报告错误、获取源码位置、执行自动修复等)。

3)、执行规则

ESLint 中的 parser 解析器解析源码后,将它转换成 AST ,遍历各个节点,检查节点是否有与该节点类型相关的规则处理函数,如果存在则调用规则处理函数进行处理,并将当前节点参数传递给它。

no-var 规则为例,当遍历到 VariableDeclaration 节点时,no-var 规则的处理函数会被调用,且会将当前的节点和节点上下文传递给处理函数,如果生成报告后需要 fix 它会调用 replaceText 函数进行修复,代码被修复成想要的格式。

  VariableDeclaration(node) {
        if (node.kind === "var") {
          context.report({
            node,
            message: "Unexpected var, use let or const instead.",
            fix(fixer) {
              return fixer.replaceText(node, "let"); // 自动修复代码
            }
          });
        }
      }

相信看到这里,我们应该对 rules 的运行原理有个更加清晰的认识。 rules 的作用是:**rules**** 用于启用或禁用特定的 ESLint 规则,或对规则进行自定义配置**。规则的值可以是 "off"(禁用)、"warn"(警告)、"error"(错误),并可以附带具体的配置选项 。<font style="color:#DF2A3F;">它配置的规则在整个配置文件中优先级也是最高的</font>。

三)、ESLint 中 extends 选项解析

在 ESLint 中,extends 关键字<font style="color:#117CEE;">用于继承现有的配置</font>。这使得你可以基于某个预设配置或者其他已经定义好的配置文件,快速建立自己的 ESLint 配置,而不需要从头开始。<font style="color:#117CEE;">这种继承机制可以帮助你避免重复配置</font>

extends 允许继承官方推荐配置 eslint:recommended继承插件推荐配置,比如常见的 plugin:vue/recommended。也支持配置自己创建的 ESLint 配置模块,Element Plus 组件库中就是在根目录中继承在子项目中配置 ESLint 配置模块,想要在多个项目中使用可以将自己配置的 ESLint 可以发布 npm 包在 extends中引入插件推荐的配置。

extends中配置多个配置项时,这些在extends中的配置会按照<font style="color:#117CEE;">由下置上的优先级顺序</font>检测,如果配置项之间的规则有冲突时,<font style="color:#117CEE;">优先级高的会覆盖优先级低的配置项并合并成一个配置规则</font>。配置项的名称遵循以下规则。

  • eslint: 开头的代表是以 ESLint 自身的规则。
  • plugin: 开头的代表以 ESLint 插件的规则,plugin:vue/vue3-recommended**/**** 后面的 **vue3-recommended** 表示插件推荐的 ESLint 规则名称**。

了解以上这些,我们再来看看 ELement Plus 中的 ESlint 配置项 extends 中的配置,应该就很容易理解了。

 extends: [
    'eslint:recommended', //使用eslint推荐的规则
    'plugin:import/recommended', //使用eslint-plugin-import 推荐的规则
    'plugin:eslint-comments/recommended', //使用eslint-plugin-comments-recommended推荐的规则
    'plugin:jsonc/recommended-with-jsonc', //使用eslint-plugin-jsonc推荐的规则,适用于JSONC(json with Comments)
    'plugin:markdown/recommended', //使用eslint-plugin-markdown 插件推荐的规则
    'plugin:vue/vue3-recommended', //使用eslint-plugin-vue 推荐Vue3的规则
    'plugin:@typescript-eslint/recommended', //使用@typescript-eslint 推荐的规则
    'prettier', //使用prettier配置,关闭所有与prettier冲突的eslint规则
  ],

四)、ESLint 其他配置项解析

配置项的说明在代码块中举例说明。


{
  //env 用于指定代码运行的环境,从而启用特定环境的全局变量和规则。
  "env": {
    "browser": true, // 启用浏览器环境的全局变量 (window, document 等)
    "node": true,    // 启用 Node.js 环境的全局变量 (require, module 等)
    "es6": true      // 启用 ES6 环境,默认支持 ES6 全局变量
  },
  //parser 用于指定代码的解析器
   "parser": "@typescript-eslint/parser" ,// 使用 TypeScript 的解析器
  //parserOptions 用于配置解析器的选项,如 ECMAScript 版本、模块类型、是否允许 JSX 语法
   "parserOptions": {
    "ecmaVersion": 2020,                // 支持 ES2020 语法
    "sourceType": "module",             // 使用 ES6 模块语法
    "ecmaFeatures": {
      "jsx": true                       // 支持 JSX 语法
    }
  }
  //plugins 用于加载额外的 ESLint 插件,这些插件可以提供额外的规则、环境定义、或解析器
  "plugins": [
    "vue",              // 加载 eslint-plugin-vue 插件
    "@typescript-eslint" // 加载 @typescript-eslint 插件
  ],
  //overrides 用于为特定的文件或文件类型设置不同的配置
   "overrides": [
    {
      "files": ["*.ts", "*.tsx"], // 仅对 TypeScript 文件应用以下规则
      "rules": {
        "@typescript-eslint/no-unused-vars": "error" // TypeScript 特定的规则
      }
    },
    {
      "files": ["*.vue"],          // 仅对 Vue 文件应用以下规则
      "rules": {
        "vue/html-indent": ["error", 2] // Vue 文件中的 HTML 缩进规则
      }
    }
  ],
  //globals 用于定义项目中的全局变量,并指定这些变量是否可写
   "globals": {
    "jQuery": "readonly", // jQuery 作为只读全局变量
    "$": "readonly"       // $ 作为只读全局变量
  },
   //settings 用于为ESlint配置一些全局设置,它可以用来提供一些额外的配置选项
  "settings": {
    //这个主要是配置了eslint-plugin-import 插件,主要用于指定拓展名来解析模块路径
    "import/resolver": {
      //node是该插件的内置解析器
      "node": { extensions: ['.js', '.mjs', '.ts', '.d.ts', '.tsx'] },
    },
  },
 //root 是一个布尔值,用于指示当前配置文件是否为项目的根配置文件。
  "root": true, // 将当前配置文件设为项目根配置文件
 //ignorePatterns 用于指定 ESLint 应忽略的文件或目录,类似于 .eslintignore 文件
的功能
"ignorePatterns": ["node_modules/", "dist/", "*.min.js"]
}
}

三、Prettier 、EditorConfig代码编辑器配置

一)、Prettier<font style="color:rgba(62,175,124,1);"></font>

1、Prettier 的介绍

Prettier 是一个<font style="color:#117CEE;">代码格式化工具</font>,因为每个人写的代码风格参差不齐,需要制订一套代码规范,但是一个个记下来并实施起来太复杂了,Prettier 工具就能解决这个麻烦,直接按照配置好的规则格式化代码,让代码统一风格。

通常 ESLint 和 Prettier 搭配使用,ESLint 中有部分的代码风格的规则检测会与 Prettier 有冲突,要解决这种冲突,可以利用前面的 **<font style="color:rgba(62,175,124,1);">extends</font>**配置项,<font style="color:rgb(51, 51, 51);">我们可以使用 </font>**<font style="color:rgb(62, 175, 124);">eslint-config-prettier</font>**<font style="color:rgb(51, 51, 51);"> </font><font style="color:#117CEE;">来关掉所有和 Prettier 冲突的 ESLint 的配置项</font><font style="color:rgb(51, 51, 51);">。具体就是在配置文件里面将 prettier 设为最后一个 extends</font>,优先级最高解决冲突。事实上 Element Plus 也是这样做的。

2、Prettier 在 ESLint 中的配置<font style="color:#2F8EF4;background-color:#FFFFFF;"></font>

实际上 **<font style="color:rgb(62, 175, 124);">eslint-config-prettier</font>** 解决规则冲突主要是通过把所有有冲突规则直接关闭,但是这样做想要执行 ESLint 的修复命令时,那些被关闭的规则无法被修复,这个时候,我们需要<font style="color:rgb(51, 51, 51);">安装 </font>**<font style="color:rgb(62, 175, 124);">eslint-plugin-prettier</font>**<font style="color:rgb(62, 175, 124);">, </font><font style="color:rgb(51, 51, 51);"> 在 ESLint 的 </font><font style="color:rgb(51, 51, 51);">plugins</font><font style="color:rgb(51, 51, 51);"> 配项中配置 。</font>

<font style="color:rgb(51, 51, 51);"></font>

pnpm i  eslint-config-prettier eslint-plugin-prettier

<font style="color:rgb(51, 51, 51);"></font>

<font style="color:rgb(51, 51, 51);">引入 ESLint 的 Prettier 插件后,需要在 rules 配置项中配置规则 </font>**<font style="color:rgb(62, 175, 124);">'prettier/prettier': 'error'</font>**<font style="color:rgb(62, 175, 124);"> </font>****<font style="color:#000000;">。</font><font style="color:#000000;">完成这些配置后,ESLint 就能愉快的使用 Prettier 了,使用 </font><font style="color:#000000;">--fix</font><font style="color:#000000;"> ESLint 命令时,也可以进行代码修复。</font>

<font style="color:#000000;"></font>

{
  plugins: ['prettier'], // 引入的是这个插件 eslint-plugin-prettier
    extends: ["prettier"], // prettier 必须是最后一个,才能确保覆盖,引入的是eslint-config-prettier 这个包
    rules: {
    // prettier 配置规则,开启 prettier 规则
    'prettier/prettier': 'error',
      }
}

3、Element Plus 中配置 Prettier

配置 Prettier 之前,我们先在 vscode 安装 prettier 插件。

[图片上传失败...(image-ccd28a-1733477351140)]

在 Element Plus 根目录中配置 prettier 配置文件,同时也可以配置 prettier 的忽略文件。我们需要实现的效果是,保存文件后直接格式化代码。

[图片上传失败...(image-60fb35-1733477351

dist
node_modules
pnpm-lock.yaml
docs/components.d.ts

二)、EditorConfig 代码编辑器配置

由于不同的代码风格可能导致不必要的代码冲突(如缩进、行尾符号的不同),EditorConfig 可以帮助减少这些冲突。Prettier 默认会与 EditorConfig 的配置文件进行合并配置,同样它也需要安装 vscode 插件。

[图片上传失败...(image-4844f-1733477351140)]

Element Plus 组件中配置如下。

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
quote_type = single

四、ELement Plus 组件库中 ESLint 配置

一)、初始化 ESLint 配置文件

Element Plus 中在项目的根目录下配置了 ESLint 的根配置文件 .eslintrc.json文件,在根目录下的创建 internal内置文件夹,在文件夹下初始化 eslint-config 项目目录,并安装这个模块到根目录 package.json 文件下。

{
  "root": true,
  "extends": ["@fz-mini/eslint-config"]
}

[图片上传失败...(image-41f3c-1733477351140)]

在这个 eslint-config目录下安装如下依赖包。

pnpm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-define-config eslint-plugin-eslint-comments eslint-plugin-import eslint-plugin-jsonc eslint-plugin-markdown eslint-plugin-prettier eslint-plugin-unicorn eslint-plugin-vue jsonc-eslint-parser prettier yaml-eslint-parser
pnpm i eslint -D
##ESlint安装各个包的作用

@typescript-eslint/eslint-plugin:提供TypeScript代码的eslint规则
@typescript-eslint/parser:将TypeScript转换为ESTree,使eslint能够分析TypeScript代码
eslint-config-prettier:禁用不必要或和prettier有冲突的ESlint规则
eslint-define-config:提供一种定义ESlint配置的方式,具有类型提示
eslint-plugin-eslint-comments:ESlint插件,用于遵循、启用、禁用eslint的注释规则
eslint-plugin-import:支持ES6导入/导出语法的ESLint插件,用于确保导入路径的正确性
eslint-plugin-jsonc:用于JSONC文件的ESlint插件
eslint-plugin-markdown:支持在Markdown文件中运行eslint规则
eslint-plugin-prettier:将Prettier集成到ESlint中,以便在ESlint中运行Prettier规则
eslint-plugin-unicorn:一组基于最佳实践的ESlint规则集,帮助改进代码质量
eslint-plugin-vue:针对vue的ESlint的插件,提供Vue特有的规则
jsonc-eslint-parser:用于解析JSONC文件的ESlint的解析器
prettier:一个代码的格式化工具
yaml-eslint-parser:用于解析YAML文件的ESlint解析器

在项目根目录下的 package.json开发环境安装这个 **@fz-mini/eslint-config** 模块,才能让根目录中的 ESLint 配置文件访问到这些配置项。<font style="color:rgba(20,204,89,1);">同时在根目录 </font>**<font style="color:rgba(20,204,89,1);">package.json</font>**<font style="color:rgba(20,204,89,1);">中开发环境中安装 </font>**<font style="color:rgba(20,204,89,1);">prettier</font>**<font style="color:rgba(20,204,89,1);"> 、</font>**<font style="color:rgba(20,204,89,1);">eslint-define-config</font>**<font style="color:rgba(20,204,89,1);"> 、</font>**<font style="color:rgba(20,204,89,1);">eslint</font>**

[图片上传失败...(image-996bb1-1733477351140)]

二)、组件中 ESLint 详细配置

Element Plus 中是在 eslint-config/index.js 文件下配置 ESLint 的配置项,接下来我会逐一解释配置项中的配置具体作用。

const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
  env: {
    es6: true, //允许ES6全局变量(例如Set)
    browser: true, //允许浏览器使用全局变量(例如:window)
    node: true, //允许Node.js全局变量和作用域(例如:process)
  },
  plugins: ['@typescript-eslint', 'prettier', 'unicorn'], //使用ESlint的插件
  //extends 中的多个模块,如果模块之间有冲突,位置靠后的模块的规则将会覆盖前面的,rules声明的规则优先级是高于extends的
  extends: [
    'eslint:recommended', //使用eslint推荐的规则
    'plugin:import/recommended', //使用eslint-plugin-import 推荐的规则
    'plugin:eslint-comments/recommended', //使用eslint-plugin-comments-recommended推荐的规则
    'plugin:jsonc/recommended-with-jsonc', //使用eslint-plugin-jsonc推荐的规则,适用于JSONC(json with Comments)
    'plugin:markdown/recommended', //使用eslint-plugin-markdown 插件推荐的规则
    'plugin:vue/vue3-recommended', //使用eslint-plugin-vue 推荐Vue3的规则
    'plugin:@typescript-eslint/recommended', //使用@typescript-eslint 推荐的规则
    'prettier', //使用prettier配置,关闭所有与prettier冲突的eslint规则
  ],
  //settings 用于为ESlint配置一些全局设置,它可以用来提供一些额外的配置选项
  settings: {
    //这个主要是配置了eslint-plugin-import 插件,主要用于指定拓展名来解析模块路径
    'import/resolver': {
      //node是该插件的内置解析器
      node: { extensions: ['.js', '.mjs', '.ts', '.d.ts', '.tsx'] },
    },
  },
  //overrides,外侧配置的rule 一般都是全局生效,通过overrides,可以针对一些文件覆盖一些规则
  overrides: [
    {
      files: ['*.json', '*.json5', '*.jsonc'],
      parser: 'jsonc-eslint-parser', //使用jsonc-eslint-parser来解析这些文件
    },
    {
      files: ['*.ts', '*.vue'], //针对TypeScript 和Vue文件的配置
      rules: {
        'no-undef': 'off',
      },
    },
    {
      files: ['**/__test__/**'],
      rules: {
        'no-console': 'off', //允许在测试中使用console
        'vue/one-component-per-file': 'off', //关闭Vue规则,允许每个文件中包含多个组件
      },
    },
    {
      files: ['package.json'], //针对package.json 文件的配置
      parser: 'jsonc-eslint-parser',
      rules: {
        'jsonc/sort-keys': [
          'error',
          {
            pathPattern: '^$',
            //对根级别属性排序
            order: [
              'name',
              'version',
              'private',
              'packageManager',
              'description',
              'type',
              'keywords',
              'homepage',
              'bugs',
              'license',
              'author',
              'contributors',
              'funding',
              'files',
              'main',
              'module',
              'exports',
              'unpkg',
              'jsdelivr',
              'browser',
              'bin',
              'man',
              'directories',
              'repository',
              'publishConfig',
              'scripts',
              'peerDependencies',
              'peerDependenciesMeta',
              'optionalDependencies',
              'dependencies',
              'devDependencies',
              'engines',
              'config',
              'overrides',
              'pnpm',
              'husky',
              'lint-staged',
              'eslintConfig',
            ],
          },
          {
            pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies$', //对依赖进行排序
            order: { type: 'asc' },
          },
        ],
      },
    },
    {
      files: ['*.d.ts'],
      rules: {
        'import/no-duplicates': 'off',
      },
    },
    {
      files: ['*.js'],
      rules: {
        '@typescript-eslint/no-var-requires': 'off',
      },
    },
    {
      files: ['*.vue'],
      parser: 'vue-eslint-parser', //解析vue文件
      parserOptions: {
        parser: '@typescript-eslint/parser', //使用@typescript-eslint/parser 解析ts文件
        extraFileExtensions: ['.vue'], //额外支持.vue 文件拓展名
        ecmaVersion: 'latest', //使用最新版本的ECMAScript版本
        ecmaFeatures: {
          jsx: true, //支持jsx语法
        },
      },
      rules: {
        'no-undef': 'off',
      },
    },
    {
      files: ['**/*.md/*.js', '**/*.md/*.ts'], //针对Markdown 文件中嵌入的JavaScript 和TypeScript 代码配置
      rules: {
        'no-console': 'off',
        'import/no-unresolved': 'off',
        '@typescript-eslint/no-unused-vars': 'off',
      },
    },
  ],
  rules: {
    camelcase: ['error', { properties: 'never' }], // 强制使用 camelCase 命名,允许属性名不遵循此规则
    'no-console': ['warn', { allow: ['error'] }], // 警告使用 console,但允许 console.error
    'no-debugger': 'warn', // 警告使用 debugger
    'no-constant-condition': ['error', { checkLoops: false }], // 禁止在条件中使用常量表达式,但允许在循环中使用
    'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'], // 禁止使用特定语法
    'no-return-await': 'error', // 禁止在 return 语句中使用 await
    'no-var': 'error', // 禁止使用 var 声明变量
    'no-empty': ['error', { allowEmptyCatch: true }], // 禁止空块,但允许空的 catch 子句
    'prefer-const': [
      'warn',
      { destructuring: 'all', ignoreReadBeforeAssign: true }, // 尽量使用 const 声明
    ],
    'prefer-arrow-callback': [
      'error',
      { allowNamedFunctions: false, allowUnboundThis: true }, // 尽量使用箭头函数
    ],
    'object-shorthand': [
      'error',
      'always',
      { ignoreConstructors: false, avoidQuotes: true }, // 强制使用对象字面量简写
    ],
    'prefer-rest-params': 'error', // 强制使用剩余参数代替 arguments
    'prefer-spread': 'error', // 强制使用扩展运算符而非 apply
    'prefer-template': 'error', // 强制使用模板字面量代替字符串连接

    'no-redeclare': 'off', // 关闭 no-redeclare 规则
    '@typescript-eslint/no-redeclare': 'error', // 使用 @typescript-eslint/no-redeclare 规则

    // 最佳实践
    'array-callback-return': 'error', // 强制数组方法的回调函数中有 return 语句
    'block-scoped-var': 'error', // 强制块作用域
    'no-alert': 'warn', // 警告使用 alert、confirm 和 prompt
    'no-case-declarations': 'error', // 禁止在 case 子句中声明变量
    'no-multi-str': 'error', // 禁止使用多行字符串
    'no-with': 'error', // 禁止使用 with 语句
    'no-void': 'error', // 禁止使用 void 操作符

    'sort-imports': [
      'warn',
      {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
        memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
        allowSeparatedGroups: false, // 强制导入排序
      },
    ],

    // 风格问题
    'prefer-exponentiation-operator': 'error', // 强制使用指数操作符 ** 代替 Math.pow

    // TypeScript 特定规则
    '@typescript-eslint/explicit-module-boundary-types': 'off', // 关闭显式模块边界类型规则
    '@typescript-eslint/no-explicit-any': 'off', // 关闭禁止使用 any 类型规则
    '@typescript-eslint/no-non-null-assertion': 'off', // 关闭禁止
    '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', //允许在可选链中使用非空断言(!)
    '@typescript-eslint/consistent-type-imports': [
      'error',
      { disallowTypeAnnotations: false },
    ], //强制类型导入的一致性,确保代码中类型导入的一致风格,但允许类型注解中使用类型导入。
    '@typescript-eslint/ban-ts-comment': ['off', { 'ts-ignore': false }], //不禁止使用 @ts- 注释(如 @ts-ignore)

    //vue
    'vue/no-v-html': 'off', //允许使用 v-html 指令。
    'vue/require-default-prop': 'off', //不要求为每个 prop 提供默认值。
    'vue/require-explicit-emits': 'off', //不强制要求显式定义组件的所有 emit 事件。
    'vue/multi-word-component-names': 'off', //允许单词的组件名称。
    'vue/prefer-import-from-vue': 'off', //不强制要求从 vue 模块中导入而不是从其他路径。
    'vue/no-v-text-v-html-on-component': 'off', //允许在组件上同时使用 v-text 和 v-html
    'vue/html-self-closing': [
      //强制 HTML、SVG 和 MathML 中的标签自闭合:
      'error',
      {
        html: {
          void: 'always',
          normal: 'always',
          component: 'always',
        },
        svg: 'always',
        math: 'always',
      },
    ],

    // prettier
    'prettier/prettier': 'error', //代码不符合 Prettier 的格式,将触发错误。

    //import
    'import/first': 'error', //确保所有的导入语句在文件的顶部。
    'import/no-duplicates': 'error', //禁止重复导入同一个模块
    //强制导入顺序:builtin: 内置模块。external: 外部模块。internal: 内部模块。parent: 父级模块。sibling: 同级模块。index: 索引模块。object: 对象模块。type: 类型模块。
    'import/order': [
      'error',
      {
        groups: [
          'builtin',
          'external',
          'internal',
          'parent',
          'sibling',
          'index',
          'object',
          'type',
        ],
        //指定 vue 模块和 @vue/** 模块优先于其他外部模块导入,@fz-mini/** 模块作为内部模块导入。
        pathGroups: [
          {
            pattern: 'vue',
            group: 'external',
            position: 'before',
          },
          {
            pattern: '@vue/**',
            group: 'external',
            position: 'before',
          },
          {
            pattern: '@fz-mini/**',
            group: 'internal',
          },
        ],
        pathGroupsExcludedImportTypes: ['type'],
      },
    ],
    'import/no-unresolved': 'off', //不强制要求导入路径的解析
    'import/namespace': 'off', //不强制命名空间导入。
    'import/default': 'off', //不强制默认导入
    'import/no-named-as-default': 'off', //允许命名导入作为默认导入。
    'import/no-named-as-default-member': 'off', //,允许命名导入作为默认成员导入。
    'import/named': 'off', //不强制命名导入的检查。

    // eslint-plugin-eslint-comments
    'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],

    'unicorn/custom-error-definition': 'error', // 强制要求自定义错误类必须继承自 Error 类
    'unicorn/error-message': 'error', // 强制要求所有抛出的错误必须包含错误消息
    'unicorn/escape-case': 'error', // 强制要求使用小写的转义字符(如 \x20 而不是 \X20)
    'unicorn/import-index': 'error', // 强制要求显式导入索引文件(如 './foo/index.js' 而不是 './foo')
    'unicorn/new-for-builtins': 'error', // 强制要求使用 new 关键字实例化内置对象(如 Date, Map 等)
    'unicorn/no-array-method-this-argument': 'error', // 禁止在数组方法(如 map, forEach)中使用 this 参数
    'unicorn/no-array-push-push': 'error', // 禁止连续调用 Array.push 方法,推荐一次性添加所有元素
    'unicorn/no-console-spaces': 'error', // 禁止在 console.log 中使用多个连续空格
    'unicorn/no-for-loop': 'error', // 禁止使用传统的 for 循环,推荐使用 for-of 或其他迭代方法
    'unicorn/no-hex-escape': 'error', // 禁止使用十六进制转义字符,推荐使用 Unicode 转义
    'unicorn/no-instanceof-array': 'error', // 禁止使用 instanceof Array,推荐使用 Array.isArray
    'unicorn/no-invalid-remove-event-listener': 'error', // 禁止使用无效的函数引用来调用 removeEventListener
    'unicorn/no-new-array': 'error', // 禁止使用 new Array() 创建数组,推荐使用数组字面量 []
    'unicorn/no-new-buffer': 'error', // 禁止使用 new Buffer(),推荐使用 Buffer.from 或 Buffer.alloc
    'unicorn/no-unsafe-regex': 'off', // 关闭规则,不检查正则表达式的安全性
    'unicorn/number-literal-case': 'error', // 强制要求数字字面量使用一致的小写字母(如 0xFF 应为 0xff)
    'unicorn/prefer-array-find': 'error', // 推荐使用 Array.find 代替 Array.filter()[0]
    'unicorn/prefer-array-flat-map': 'error', // 推荐使用 Array.flatMap 代替 Array.map().flat()
    'unicorn/prefer-array-index-of': 'error', // 推荐使用 Array.indexOf 代替 Array.findIndex
    'unicorn/prefer-array-some': 'error', // 推荐使用 Array.some 代替 Array.filter().length > 0
    'unicorn/prefer-date-now': 'error', // 推荐使用 Date.now() 代替 (new Date()).getTime()
    'unicorn/prefer-dom-node-dataset': 'error', // 推荐使用 dataset 属性代替 getAttribute 和 setAttribute
    'unicorn/prefer-includes': 'error', // 推荐使用 String.includes 或 Array.includes 代替 indexOf
    'unicorn/prefer-keyboard-event-key': 'error', // 推荐使用 KeyboardEvent.key 代替 KeyboardEvent.keyCode
    'unicorn/prefer-math-trunc': 'error', // 推荐使用 Math.trunc 代替 Math.floor, Math.ceil, Math.round
    'unicorn/prefer-modern-dom-apis': 'error', // 推荐使用现代 DOM API(如 querySelector)代替旧 API
    'unicorn/prefer-negative-index': 'error', // 推荐使用负索引来从数组末尾访问元素(如 array[-1])
    'unicorn/prefer-number-properties': 'error', // 推荐使用 Number 的静态属性代替全局函数(如 Number.isNaN 代替 isNaN)
    'unicorn/prefer-optional-catch-binding': 'error', // 推荐使用可选的 catch 绑定(省略 catch 参数)
    'unicorn/prefer-prototype-methods': 'error', // 推荐使用原型方法(如 Array.prototype.indexOf.call 代替 Array.indexOf)
    'unicorn/prefer-query-selector': 'error', // 推荐使用 querySelector 和 querySelectorAll 代替 getElementById 等
    'unicorn/prefer-reflect-apply': 'error', // 推荐使用 Reflect.apply 代替 Function.prototype.apply
    'unicorn/prefer-string-slice': 'error', // 推荐使用 String.slice 代替 substr 和 substring
    'unicorn/prefer-string-starts-ends-with': 'error', // 推荐使用 String.startsWith 和 String.endsWith 代替 indexOf
    'unicorn/prefer-string-trim-start-end': 'error', // 推荐使用 String.trimStart 和 String.trimEnd 代替 String.trim
    'unicorn/prefer-type-error': 'error', // 推荐抛出 TypeError 错误类型代替其他错误类型
    'unicorn/throw-new-error': 'error', // 推荐使用 new Error 抛出错误,确保抛出的错误是一个 Error 实例
  },
})

配置好这些 ESLint 配置项后,可以在根目录中的 package.json文件中配置执行命令。命令行作用:检测这些文件、修复代码不符合规范的代码。****

"scripts":{
   "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.md,.json --max-warnings 0 --cache",
   "lint:fix": "pnpm run lint --fix",
},

保存后编辑器相关配置,在根目录下创建.vscode/settings.json文件,默认保存后触发格式化校验。

{
  "editor.formatOnSave": true,
  "editor.formatOnType": false,
  "prettier.trailingComma": "none",
  "eslint.probe": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "markdown",
    "json",
    "jsonc"
  ],
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "markdown",
    "json",
    "jsonc"
  ]
}

输入不符合规范的代码后会出现爆红或者警告,点击保存后会自动格式化说明配置成功。VSCode 编辑器需要安装对应的插件 **ESLint****Prettier****EditorConfig**,否则也没有具体效果。

三)、代码提交前的校验 lint-stage、husky

1、husky

Husky 是一个用于 Git 钩子的工具,可以帮助开发者在特定的 Git 操作(如提交、推送、合并等)之前或之后自动执行脚本。通过 Husky,团队可以在项目中强制执行代码质量检查、自动化任务、或其他开发流程,确保代码库的稳定性和一致性.

安装 Husky:

pnpm install husky -D -w

自动配置 Husky

pnpm dlx husky-init

在 package.json 文件的 scripts 中创建一条脚本命令:"prepare": "husky"

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

在运行 pnpm dlx husky-init 命令之后同时也在根目录下创建 .husky 目录和相关文件。

[图片上传失败...(image-cd6358-1733477351140)]

这个时候我们就可以在 pre-commit 文件中配置一些脚本命令了,让这些脚本命令在 git commit 之前执行。

2、lint-stage

是一个用于优化代码检查和格式化的工具,通常与 Git 钩子工具(如 Husky)结合使用。它的主要作用是在提交代码时,只对 Git 暂存区(staged)的文件进行检查和格式化,而不是对整个代码库运行这些操作,从而提高了代码检查和修复的效率。 。

安装 lint-staged

pnpm install lint-staged -D -w

然后在 package.json 文件进行配置:

"lint-staged": {
  "*.{vue,js,ts,jsx,tsx,md,json}": "eslint --fix"
}

接着在刚从创建的 .husky 目录中的 pre-commit 文件中配置如下脚本:

pnpm exec lint-staged

pnpm exec 用于在当前项目的环境中运行本地安装的可执行文件。它的作用类似于 npm runyarn run 中的 npx,但专门用于 pnpm 的工作流 。

五、总结

本章节篇幅有点多,如果只想看 Element Plus 配置项如何实现的,可以直接跳到相关目录查看。如有不准确的请多多指正,一起进步。 项目源码细节请移步https://github.com/5fyt/fz-mini-ui

  • ESLint 如何寻找配置文件
  • ESLint 生成 AST、ESLint 配置项 rules 的运行原理
  • ESLint 中 extends 选项的具体解析
  • ESLint 配置文件中其他配置项的简单解析
  • Prettier 和 EditorConfig 的基本配置
  • Element Plus 中的 ESLint 配置项实现
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容