@vue/cli-plugin-eslint:为 Vue CLI 项目接入 ESLint 代码规范检查与自动修复

原创2026-09-18 22:38:171,969 阅读
文章标签:前端开发工具构建工具

@vue/cli-plugin-eslint:为 Vue CLI 项目接入 ESLint 代码规范检查与自动修复

本文基于 vue-cli 仓库中 docs/ru/core-plugins/eslint.md 与 docs/core-plugins/eslint.md 整理而成,并结合 @vue/cli-plugin-eslint 包源码进行深度印证。文章面向使用 Vue CLI 5 创建项目的开发者,讲解如何通过 @vue/cli-plugin-eslint 为项目注入 vue-cli-service lint 命令、配置 lintOnSave 实现保存即检查,以及该插件的底层 webpack 集成原理,让读者能够完整掌握 Vue CLI 项目中 ESLint 的接入、配置与调优方法。

插件定位:vue-cli 的官方 ESLint 集成方案

@vue/cli-plugin-eslint 是 vue-cli 生态中负责代码规范检查(Linting)与自动修复(Auto-fix)的官方插件。它的核心职责可以概括为三件事:

  1. 在 vue create 创建项目时或通过 vue add eslint 向已有项目注入一套完整的 ESLint 配置、依赖与 npm scripts;
  2. 向 @vue/cli-service 注册一条 vue-cli-service lint 命令,用于命令行批量检查与修复代码;
  3. 通过 webpack 插件在开发服务器和构建流程中启用“保存即检查”(Lint-on-save),并可选地把检查结果以 overlay 形式呈现在浏览器中。

从包结构看(packages/@vue/cli-plugin-eslint/package.json),插件本身依赖 eslint-webpack-plugin、globby、yorkie(Git hooks 工具)与 @vue/cli-shared-utils,并以 eslint >= 7.5.0 作为 peer 依赖——也就是说,ESLint 本体并不由插件打包,而是安装在项目自己的依赖树中,用户完全可以根据需要锁定 ESLint 版本。

安装与初始化:两种接入路径

路径一:创建项目时选择

在 vue create 的交互式功能列表中勾选 Linter / Formatter 即可随项目脚手架一并安装。插件会以交互问题(prompts)的形式收集偏好,见 packages/@vue/cli-plugin-eslint/prompts.js:

  • Pick an ESLint config(单选):Error prevention only(base,仅做基础错误预防)、Airbnb、Standard、Prettier;
  • Pick additional lint features(多选):Lint on save(保存时检查,默认勾选)与 Lint and fix on commit(提交前检查修复,需要 Git 环境)。

选择结果会被写入 package.json:生成 lint: vue-cli-service lint 脚本、eslintConfig 配置块以及对应 preset 的 devDependencies,具体实现见 packages/@vue/cli-plugin-eslint/generator/index.js。

路径二:已有项目接入

原文档给出了一条命令即可完成向已有项目注入插件:

vue add eslint

vue add 会调用该插件的 generator,此时同样会弹出上述两个 prompts 让你选择配置预设与 lint 特性。源码中还有两个值得注意的细节:

  • 若未勾选 “Lint on save”,generator 会在 package.json 的 vue 字段中写入 lintOnSave: false(见 generator/index.js);
  • 若勾选 “Lint and fix on commit”,会追加 lint-staged 依赖,并通过 yorkie 写入 gitHooks: { 'pre-commit': 'lint-staged' },同时根据插件支持的扩展名生成 lint-staged 匹配规则,例如 *.{js,jsx,vue} 对应的命令为 vue-cli-service lint。

项目创建完成后,generator 还会以静默模式({ silent: true })自动执行一次 lint,把脚手架生成的代码统一修复为所选代码风格(见 generator/index.js 的 afterAnyInvoke hooks)。测试用例 eslintPlugin.spec.js 中即验证了 “airbnb 预设下自动补全分号、提交前 lint-staged 自动修复” 的完整链路。

注入命令:vue-cli-service lint 完全指南

安装插件后,项目即可使用 vue-cli-service lint 对源码进行批量检查与自动修复。原文档给出了命令的帮助信息:

Usage: vue-cli-service lint [options] [...files]

Options:

  --format [formatter] 指定格式化器 (默认: stylish)
  --no-fix             关闭自动修复
  --max-errors         错误数达到该值则构建失败 (默认: 0)
  --max-warnings       警告数达到该值则构建失败 (默认: Infinity)

在英文版文档中还有一条:

  --output-file        指定报告输出文件

默认检查范围

如果不显式传入文件列表,vue-cli-service lint 默认检查:

  • src 目录下的所有文件;
  • tests 目录下的所有文件;
  • 根目录下所有 JavaScript 文件——最常见的便是 babel.config.js、.eslintrc.js 等配置文件。

这一默认行为对应源码 lint.js 中的 defaultFilesToLint 计算逻辑:依次对 src、tests、*.js、.*.js 四个模式进行 globby 匹配,并用 eslint.isPathIgnored() 过滤掉被忽略的目录,最终保留存在实际检查对象的模式。

关于点文件的提示(原文档 tip)

默认情况下 vue-cli-service lint 会检查形如 .*.js 的点文件。
如果想遵循 ESLint 的默认行为,请在项目中添加 .eslintignore 文件。

这一行为背后的原因值得说明:ESLint 默认会忽略点文件(dotfiles),但 Vue CLI 项目希望 babel.config.js 这类配置文件也能参与 lint 与自动修复,以保证默认脚手架生成的代码风格与用户选定的 ESLint 配置一致。因此在 lint.js 中,当项目不存在 .eslintignore 且用户未提供 --ignore-pattern 时,插件会向 overrideConfig.ignorePatterns 注入 !.*.js 与 !{src,tests}/**/.*.js 两条否定模式,把点文件“拉回”检查范围。一旦用户提供了自己的 .eslintignore,插件便不再注入自定义忽略规则(遵循最小惊讶原则)。

更多受支持的 ESLint CLI 选项

原文档提到该命令还支持其他 ESLint CLI 选项。从 lint.js 的 normalizeConfig 可以看出,这些 CLI 参数会被映射为 ESLint 的编程式配置,实际支持项包括:

命令行参数 映射目标 说明
--no-fix-warnings fixWarnings: false 只修复错误、不修复警告
--ext [extensions] extensions 指定待检查的文件扩展名列表(逗号分隔)
--rulesdir [paths] rulePaths 附加的规则目录
--plugin [name] overrideConfig.plugins 追加 ESLint 插件
--ignore-pattern [pattern] overrideConfig.ignorePatterns 追加忽略模式
--env [name:true/false] overrideConfig.env 设置环境变量
--global [name:true/false] overrideConfig.globals 设置全局变量
--rule [name:level] overrideConfig.rules 覆盖规则级别
--inline-config allowInlineConfig 是否允许行内注释配置
--eslintrc useEslintrc 是否读取 .eslintrc 配置文件
-c / --config [file] overrideConfigFile 指定配置文件
--output-file [file] outputFile 将报告写入文件

命令执行流程与退出码

结合 lint.js 可以梳理出完整的执行链路:

  1. 通过 @vue/cli-shared-utils 的 loadModule 从项目目录加载 ESLint(用户可自定义 ESLint 版本),失败时回退到插件自身安装的版本;
  2. 以 cwd 为项目根目录、fix: true 为默认值构造 ESLint 实例配置;
  3. 调用 eslint.lintFiles(files) 批量检查;
  4. 若指定了 --output-file,将格式化后的报告写入该文件;
  5. 若开启了修复,调用 ESLint.outputFixes(resultResults) 把可自动修复的结果写回磁盘,并列出已自动修复的文件;
  6. 统计 errorCount 与 warningCount,与 --max-errors(默认 0)、--max-warnings(默认 Infinity)比较,超出阈值即 exit(1) 使命令以失败结束;否则输出 No lint errors found! 或 All lint errors auto-fixed.。

需要注意:命令内部会临时将 process.cwd mock 为项目根目录再执行 lint,这是为了规避 eslint-plugin-import 等依赖 process.cwd() 解析路径的插件在 monorepo/子目录场景下的误判(对应 lint.js 及其中引用的 vuejs/vue-cli#2554 等 issue)。

ESLint 配置:从 .eslintrc 到脚手架生成的预设

原文档指出,ESLint 可通过项目根目录的 .eslintrc 文件或 package.json 中的 eslintConfig 字段进行配置,详细语法可参考 ESLint 官方配置文档。

插件默认生成的配置骨架

当通过 Vue CLI 脚手架创建项目时,generator 会调用 eslintOptions.js 中的 config() 生成 eslintConfig,其基础结构为:

{
  root: true,                       // 停止向上级目录查找配置
  env: { node: true },              // 启用 Node 环境全局变量
  extends: ['plugin:vue/essential'],// Vue 官方推荐的必要规则
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    'no-console': "process.env.NODE_ENV === 'production' ? 'warn' : 'off'",
    'no-debugger': "process.env.NODE_ENV === 'production' ? 'warn' : 'off'"
  }
}

其中 no-console 与 no-debugger 两条规则比较有 Vue CLI 特色:它们在开发环境完全关闭,仅在 production 构建时降级为 warning,避免误伤调试输出。

不同 preset 会进一步追加 extends 与解析器:

预设 追加的 extends 额外说明
base(默认) eslint:recommended 仅基础错误预防
airbnb @vue/airbnb 需要 @vue/eslint-config-airbnb 等依赖
standard @vue/standard 无分号风格
prettier eslint:recommended + plugin:prettier/recommended 与 Prettier 格式化器协同

对应的依赖映射集中在 eslintDeps.js 的 DEPS_MAP 中,例如 base 预设安装 eslint@^7.32.0 与 eslint-plugin-vue@^8.0.3,airbnb 预设额外安装 @vue/eslint-config-airbnb 等。

与其他插件组合时的自动适配

config() 函数还处理了几种组合场景:

  • 启用 Babel 且未启用 TypeScript:parserOptions.parser 设为 @babel/eslint-parser,并自动安装 @babel/core 与 @babel/eslint-parser 依赖;
  • 启用 TypeScript:追加 @vue/typescript/recommended(Prettier 预设下会调整顺序以保证与 prettier 规则兼容),检查扩展名自动扩展为 .js, .jsx, .vue, .ts, .tsx(见 eslintOptions.js 的 extensions 导出);
  • Vue 3 项目(rootOptions.vueVersion === '3'):将 plugin:vue/essential 等规则集自动替换为 plugin:vue/vue3-essential 对应的 Vue 3 版本。

此外,ESLint 8 的迁移也由插件内置的 migrator 处理(migrator/index.js):老项目升级时,若检测到本地 ESLint 主版本小于等于 6,会自动把依赖提升到 ESLint v7、将 babel-eslint 替换为 @babel/eslint-parser,并修正 @vue/typescript 等旧式 extends 名称。

lintOnSave:开发期“保存即检查”的完整配置

原文档将 lintOnSave 归入 vue.config.js 的配置项,并强调:该选项只有在安装了 @vue/cli-plugin-eslint 时才会生效。

开发服务器中基于 eslint-loader(Vue CLI 5 实际已替换为 eslint-webpack-plugin,见下文“底层原理”)的保存即检查默认开启,可通过如下配置关闭:

// vue.config.js
module.exports = {
  lintOnSave: false
}

三种取值的行为差异

原文档给出了关键的三档取值,结合 index.js 与 packages/@vue/cli-service/lib/options.js 的校验规则(合法值为 true | false | 'error' | 'warning' | 'default'),可以整理成如下对照:

取值 处理方式 编译是否失败
true(或 'warning') 所有 lint 结果以 warning 形式发出,仅打印到终端 不失败(failOnError: false)
'error' 所有 lint 结果强制以 error 形式发出,并在浏览器 overlay 中展示 失败(failOnError: true、failOnWarning: true)
false 完全禁用 webpack 阶段的 lint —
'default' 项目默认值 —

源码中的对应逻辑为:

const treatAllAsWarnings = lintOnSave === true || lintOnSave === 'warning'
const treatAllAsErrors = lintOnSave === 'error'
const failOnWarning = treatAllAsErrors
const failOnError = !treatAllAsWarnings

也就是说,lintOnSave: true 时 eslint-webpack-plugin 的 failOnError 被置为 false,所有问题只作为警告记录在终端日志中,不会中断热更新编译;而 lintOnSave: 'error' 时则完全相反。

浏览器 overlay 的精细化控制

当使用 lintOnSave: 'error' 让错误显示在浏览器中后,还可以通过 devServer.overlay 同时控制警告与错误的展示:

// vue.config.js
module.exports = {
  devServer: {
    overlay: {
      warnings: true,
      errors: true
    }
  }
}

生产构建时关闭 lint

lintOnSave 为真值时,eslint-webpack-plugin 在开发与生产构建中都会启用。如果只想在开发阶段做保存即检查、避免拖慢生产构建,可以结合环境变量:

// vue.config.js
module.exports = {
  lintOnSave: process.env.NODE_ENV !== 'production'
}

底层原理:webpack 集成与缓存机制

原文档在结尾列出了插件注入的 webpack-chain 规则,这是理解插件工作方式的关键线索:

  • config.module.rule('eslint')
  • config.module.rule('eslint').use('eslint-loader')

需要说明的是:文档撰写时插件基于 eslint-loader 注入规则;在 Vue CLI 5 的当前实现中,插件已改用 eslint-webpack-plugin(见 packages/@vue/cli-plugin-eslint/package.json 的 dependencies 与 index.js),其职责与效果保持一致——在 webpack 编译管线中加入 ESLint 检查环节。

插件装配逻辑

index.js 展示了完整的装配流程:

  1. 读取 options.lintOnSave,为假值则跳过 webpack 集成(仅保留 lint 命令);
  2. 通过 api.genCacheConfig('eslint', { eslint: eslintPkg.version }, ['package.json']) 生成缓存标识符——把 ESLint 版本与 package.json(含依赖锁文件信息)纳入缓存 key,避免升级 ESLint 插件后命中过期缓存;
  3. 在 chainWebpack 钩子中向 webpackConfig.plugin('eslint') 注入 eslint-webpack-plugin,并传入 extensions(.js/.jsx/.vue,TypeScript 项目追加 .ts/.tsx)、cwd、cache、cacheLocation、failOnWarning/failOnError、eslintPath 等选项;
  4. 同时调用 api.registerCommand('lint', ...) 注册命令行任务。

其中 eslintPath 的解析同样遵循“项目优先”原则:优先使用项目 node_modules 中的 ESLint,找不到才回退到插件自身的依赖,这保证了用户可以自由升降级 ESLint 版本。

lint 命令与 webpack 插件的分工

  • vue-cli-service lint:一次性的全量检查 + 修复,面向 CI 与开发者手动执行;
  • eslint-webpack-plugin(保存即检查):跟随 webpack 编译持续运行,把结果映射为编译警告/错误,并驱动浏览器 overlay 展示。

两者复用同一套 eslintOptions.extensions 与项目级 ESLint 配置,因此命令行检查与开发期检查的规则口径天然一致。

升级与迁移注意事项

对于从 Vue CLI 3/4 迁移的老项目,migrator/index.js 会自动处理以下变更:

  • 将 ESLint 从 v4/v5/v6 升级到 v7,并把 ESLint 依赖从插件内部提取到项目 devDependencies;
  • 用 @babel/eslint-parser(需 @babel/core >= 7.2.0)替换已废弃的 babel-eslint;
  • TypeScript 项目将解析器统一为 @typescript-eslint/parser;
  • 依据项目已有依赖(airbnb/standard/prettier)自动补齐对应配置包的升级。

升级完成后,原有 extends: ['@vue/typescript'] 等旧式写法建议按 v7 规范迁移为 @vue/typescript/recommended(源码 TODO 注释也提示了后续计划,可关注 ESLint 官方迁移文档)。

小结

@vue/cli-plugin-eslint 将 ESLint 的能力无缝嵌入了 Vue CLI 的开发闭环:创建项目时可交互式选择代码风格预设,开发期通过 lintOnSave 实现保存即检查与浏览器 overlay 报错,提交前可借助 lint-staged 自动修复,命令行则有功能完整的 vue-cli-service lint 支撑 CI 质量门禁。理解 lintOnSave 的四种取值语义、默认检查范围与点文件规则、以及 webpack 层 lint 与命令行 lint 的分工,就能在真实项目中把代码规范从“事后检查”升级为“过程约束”。

延伸阅读

登录后查看全文
vue-cli