ESLint 和 Prettier 是前端开发中用于代码规范和格式化的两大工具,配置好之后可以对项目进行规范,不过要是不是特别严格的开发团队,我觉得就使用警告的方式并且不要自动格式化。一般编译器都可以下载插件然后自动格式化。
一、工具核心作用
1. ESLint:代码质量检查工具
- 核心功能:通过静态分析检测代码中的语法错误、逻辑问题、不符合编码规范的写法(如未使用的变量、不合理的条件判断、不符合团队约定的命名等)。
- 示例场景:
- 检测到未声明的变量(可能是拼写错误)。
- 函数内存在无法到达的代码(如
return
后的语句)。 - 强制使用
===
而非==
避免隐式类型转换。
- 灵活性:支持自定义规则(通过
.eslintrc
配置),可集成第三方规则集(如eslint-config-airbnb
、eslint-config-standard
)。
2. Prettier:代码格式化工具
- 核心功能:专注于代码的格式美化,不关心代码逻辑,仅统一代码的缩进、换行、引号、空格等样式(如强制使用单引号、末尾加分号、对象属性换行规则等)。
- 示例场景:
- 将
const a = "hello"
格式化为const a = 'hello'
(统一单引号)。 - 将过长的函数参数自动换行,确保每行代码长度不超过设定值(如 80 字符)。
- 统一数组、对象的括号前后空格(如
{ key: 1 }
而非{key:1}
)。
- 将
- 特点:规则相对固定(可配置项较少),强调 “无配置” 的开箱即用,减少团队因格式细节争论。
二、核心区别:关注点不同
维度 | ESLint | Prettier |
---|---|---|
关注内容 | 代码质量(逻辑、语法错误) | 代码格式(样式、排版) |
规则性质 | 可大量自定义(灵活) | 配置项少(强调统一) |
冲突可能性 | 可能与 Prettier 规则冲突 | 专注格式,不与 ESLint 质量规则冲突 |
典型规则示例 | 禁止未使用的变量(no-unused-vars ) |
强制使用单引号(singleQuote: true ) |
三、协同配置:解决冲突,强强联合
由于 ESLint 也包含部分格式相关的规则(如缩进、引号),可能与 Prettier 冲突(例如 ESLint 强制双引号,而 Prettier 强制单引号)。因此需要通过以下步骤协同工作:
1. 安装必要依赖
# 核心工具
npm install eslint prettier --save-dev
# 解决 ESLint 与 Prettier 的冲突
npm install eslint-config-prettier --save-dev
# 让 ESLint 能够运行 Prettier 的格式化规则(作为 ESLint 规则执行)
npm install eslint-plugin-prettier --save-dev
2. 配置文件(.eslintrc.js)
通过 eslint-config-prettier
禁用 ESLint 中与 Prettier 冲突的格式规则,再通过 eslint-plugin-prettier
将 Prettier 格式规则作为 ESLint 规则运行(即格式问题会被 ESLint 检测为错误)。
module.exports = {
extends: [
// 基础规则集(如标准规则)
'eslint:recommended',
// 禁用 ESLint 中与 Prettier 冲突的规则
'prettier',
// 让 Prettier 规则以 ESLint 规则的形式生效(必须放在最后)
'plugin:prettier/recommended'
],
plugins: ['prettier'],
rules: {
// 自定义 ESLint 质量规则(如禁止 console)
'no-console': 'warn',
// Prettier 格式规则通过 .prettierrc 配置,此处无需重复设置
}
};
3. 配置 Prettier(.prettierrc)
通过 .prettierrc
定义格式规则(可选,默认已有合理配置):
{
"singleQuote": true, // 强制单引号
"semi": true, // 语句末尾加分号
"printWidth": 80, // 每行最大字符数
"tabWidth": 2, // 缩进空格数
"trailingComma": "es5" // 对象/数组最后一个元素后加逗号(如 { a: 1, })
}
4. 忽略文件(可选)
.eslintignore
:指定 ESLint 忽略的文件(如node_modules/
)。.prettierignore
:指定 Prettier 忽略的文件(与 ESLint 忽略规则可保持一致)。
四、使用方式:自动化执行
- 命令行手动执行:
- 仅 ESLint 检查:
npx eslint src/
(检查src
目录下的文件)。 - 仅 Prettier 格式化:
npx prettier --write src/
(自动修复格式问题)。 - 结合 ESLint 执行 Prettier:
npx eslint src/ --fix
(同时修复 ESLint 可修复的问题和 Prettier 格式问题)。
- 仅 ESLint 检查:
- 编辑器集成:
- 在 VS Code 中安装
ESLint
和Prettier
插件,配置保存时自动修复(editor.formatOnSave: true
+editor.codeActionsOnSave: { "source.fixAll.eslint": true }
)。
- 在 VS Code 中安装
- 提交代码时自动检查:
- 通过
husky
+lint-staged
在代码提交前自动执行 ESLint 和 Prettier,确保提交的代码符合规范(避免不合规代码进入仓库)。
- 通过
五、总结
- ESLint 负责 “把关代码质量”,解决语法错误和逻辑问题;
- Prettier 负责 “统一代码颜值”,解决格式不统一的问题;
- 二者结合时,通过
eslint-config-prettier
和eslint-plugin-prettier
消除冲突,实现 “质量 + 格式” 双重保障,是现代前端工程化中不可或缺的工具链组合。
ESLint配置
module.exports = {
root: true,
// 环境配置
env: {
browser: true,
es2021: true,
node: true
},
// 继承的规则集
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'prettier' // 与Prettier兼容,必须放在最后
],
// 解析器配置
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaFeatures: {
jsx: true // 如不使用JSX可设为false
}
},
// 插件
plugins: [
'vue',
'@typescript-eslint',
'import',
'unused-imports' // 自动移除未使用的导入
],
// 自定义规则
rules: {
// 基础JavaScript规则
'eqeqeq': ['error', 'always'], // 强制使用===
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-var': 'error', // 禁止使用var
'prefer-const': 'error', // 优先使用const
'no-unused-vars': 'off', // 由@typescript-eslint/no-unused-vars替代
// Vue规则
'vue/script-setup-uses-vars': 'error', // 确保<script setup>中变量被使用
'vue/no-mutating-props': 'error', // 禁止修改props
'vue/attributes-order': 'warn', // 属性排序建议
'vue/no-unused-components': 'warn', // 未使用组件警告
'vue/require-prop-types': 'error', // 强制props类型定义
'vue/multi-word-component-names': ['error', {
ignores: ['index'] // 允许index作为单字组件名
}],
// TypeScript规则
'@typescript-eslint/no-unused-vars': ['warn', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
'@typescript-eslint/explicit-module-boundary-types': 'off', // 放宽导出函数返回类型检查
'@typescript-eslint/no-explicit-any': ['warn', {
ignoreRestArgs: true // 允许...args: any[]
}],
'@typescript-eslint/consistent-type-imports': 'error', // 统一使用import type
'@typescript-eslint/no-non-null-assertion': 'warn', // 非空断言警告
// 导入规则
'import/order': ['warn', {
'groups': [['builtin', 'external'], 'internal', ['parent', 'sibling', 'index']],
'newlines-between': 'always'
}],
'unused-imports/no-unused-imports': 'error', // 自动移除未使用的导入
'unused-imports/no-unused-vars': 'off',
// 其他规则
'no-trailing-spaces': 'error', // 禁止行尾空格
'eol-last': ['error', 'always'], // 文件末尾必须有空行
'quotes': 'off' // 由Prettier控制引号
},
// 全局变量
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
defineModel: 'readonly',
defineOptions: 'readonly'
},
// 针对特定文件的规则覆盖
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off' // TypeScript会处理未定义变量检查
}
}
]
};
Prettier配置
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
quoteProps: 'as-needed',
jsxSingleQuote: false,
jsxBracketSameLine: false,
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
}
ESLint 8.23+ 扁平化配置
import globals from "globals";
import pluginVue from "eslint-plugin-vue";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import importPlugin from "eslint-plugin-import";
import unusedImports from "eslint-plugin-unused-imports";
import prettier from "eslint-config-prettier";
// 环境变量配置
const env = {
browser: true,
node: true,
es2025: true,
};
// 全局变量配置
const globalVariables = {
...globals.browser,
...globals.node,
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly",
defineModel: "readonly",
defineOptions: "readonly",
};
// 基础规则配置
export default [
// 忽略文件
{
ignores: ["node_modules/**", "dist/**", "*.d.ts", "*.generated.ts"],
},
// 全局设置
{
env,
globals: globalVariables,
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
// Prettier 配置(关闭与Prettier冲突的规则)
prettier,
// Vue 规则
...pluginVue.configs["flat/essential"],
{
files: ["*.vue", "**/*.vue"],
languageOptions: {
parser: pluginVue.parser,
parserOptions: {
parser: tsParser,
ecmaVersion: "latest",
sourceType: "module",
},
},
rules: {
...pluginVue.configs["flat/recommended"].rules,
"vue/script-setup-uses-vars": "error",
"vue/no-mutating-props": "error",
"vue/attributes-order": "warn",
"vue/no-unused-components": "warn",
"vue/require-prop-types": "error",
"vue/multi-word-component-names": ["error", {
ignores: ["index"]
}],
"vue/no-v-model-argument": "off",
},
},
// TypeScript 规则
{
files: ["*.ts", "**/*.ts", "*.vue", "**/*.vue"],
plugins: {
"@typescript-eslint": tseslint,
"unused-imports": unusedImports,
},
languageOptions: {
parser: tsParser,
parserOptions: {
project: "./tsconfig.json", // 确保存在tsconfig.json
},
},
rules: {
...tseslint.configs.recommended.rules,
"@typescript-eslint/no-unused-vars": ["warn", {
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
}],
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": ["warn", {
ignoreRestArgs: true,
}],
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"unused-imports/no-unused-imports": "error",
},
},
// 导入规则
{
plugins: {
import: importPlugin,
},
rules: {
"import/order": ["warn", {
groups: [["builtin", "external"], "internal", ["parent", "sibling", "index"]],
"newlines-between": "always",
}],
},
},
// 通用JavaScript规则
{
rules: {
"eqeqeq": ["error", "always"],
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"no-var": "error",
"prefer-const": "error",
"no-unused-vars": "off", // 由TypeScript规则接管
"no-trailing-spaces": "error",
"eol-last": ["error", "always"],
},
},
];