一、前言
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 run
或 yarn 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 配置项实现