Eslint 是一个用于识别和报告在 ECMAScript/JavaScript 代码中发现的模式的工具,其目标是使代码更加一致并避免错误。
主要特点
- 性能优化:ESLint 9 对代码分析的性能进行了优化,使得运行速度更快。
- 扩展支持:改进了对第三方扩展和插件的支持,使得开发者可以更容易地自定义 ESLint 的功能。
- 配置简化:引入了更简洁的配置选项,减少了配置文件的复杂度,让新手和老手都能更方便地使用。
- 新规则:增加了多项新的代码检查规则,帮助开发者更全面地检查代码质量。
- 改进的自动修复:增强了自动修复功能,可以自动修复更多类型的代码问题,节省开发者的时间。
先决条件
要使用 ESLint,你必须安装并构建 Node.js(^18.18.0、^20.9.0 或 >=21.1.0)并支持 SSL。(如果你使用的是官方 Node.js 发行版,则始终内置 SSL。)
配置
// eslint.config.js
export default [
{
rules: {
"no-unused-vars": "error",
"no-undef": "error"
}
}
];
插件推荐
- eslint-plugin-vue Vue.js 的官方 ESLint 插件介绍
- vue-eslint-parser 专门用于解析Vue.js单文件组件(.vue文件)的ESLint插件介绍
- @typescript-eslint/parser 专门为 TypeScript 语法设计的解析器,能够解析 TypeScript 中的所有语法介绍
- @stylistic/eslint-plugin ESLint 的风格规则集合介绍
- eslint-plugin-prettier 将Prettier作为Eslint的规则运行介绍
自定义规则
当现有规则或插件无法满足时,eslint也提供了自定义功能;
详情可以查看完整配置中的eslint-rules/eslint-plugin-vue部分
- 对外暴露一个对象,其中必须要包含
meta和rules - 在
meta中规则的元信息,例如规则类型、文档和可修复性 - 添加规则访问者方法 【重点】
- 定义规则的
create函数,它接受一个context对象并返回一个对象,该对象具有你要处理的每个语法节点类型的属性。在这种情况下,你想要处理VariableDeclarator个节点。你可以选择任何 ESTree 节点类型 或 选择器。 - 通过获取到的数据通过正则或者其他插件里面的规则方法进行匹配,并处理成想要的数据
context.report()向 ESLint 报告错误。错误报告包含有关错误及其修复方法的信息
- 定义规则的
完整配置
实现了对
vue模板、ts、以及vue模板中style模块使用prettier格式化等功能,开箱即用
// eslint.config.js
const pluginVue = require("eslint-plugin-vue")
const vueParser = require("vue-eslint-parser")
const typescriptParser = require("@typescript-eslint/parser")
const stylistic = require("@stylistic/eslint-plugin")
const eslintPluginVue = require("./eslint-rules/eslint-plugin-vue") // 自定义规则
module.exports = [
{
ignores: [
"**/node_modules/**/*",
"**/dist/**/*",
"**/lib/**/*",
"**/types/**/*.d.ts",
],
files: [
"**/*.ts",
"**/*.tsx",
"**/*.vue",
"**/*.js",
"**/*.jsx",
"**/*.cjs",
],
},
{
plugins: { typescriptParser },
languageOptions: {
parser: typescriptParser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
extraFileExtensions: [".vue", ".ts", ".tsx", "css"],
},
},
},
stylistic.configs.customize({
indent: "tab",
quotes: "double",
semi: false,
jsx: true,
commaDangle: "always-multiline",
}),
...pluginVue.configs["flat/recommended"],
{
files: ["**/*.vue"],
languageOptions: {
parser: vueParser,
parserOptions: {
parser: typescriptParser,
sourceType: "module",
jsx: true,
},
},
},
{
files: ["**/*.vue"],
plugins: { "eslint-rules": eslintPluginVue },
rules: {
"eslint-rules/vue-style-prettier": "error", // vue模板中style使用prettier
},
},
{
rules: {
"linebreak-style": ["off", "lf"], // 换行符
"quotes": ["error", "double"], // 使用双引号
"eqeqeq": ["error", "smart"], // 比较的时候使用严格等于
"semi": ["error", "never"], // 不使用分号结尾
"comma-dangle": ["error", "always-multiline"], // 要求末尾逗号
"no-unused-vars": "error", // 禁止出现未使用过的变量
"default-case": "error", // 要求 switch 语句中有 default 分支
"brace-style": ["error", "stroustrup", { allowSingleLine: true }], // 大括号风格 ["error", "stroustrup"]
"no-dupe-keys": "error", // 对象中不允许出现重复的键
"no-sparse-arrays": "error", // 禁止稀疏数组, [1,,2]
"no-empty": "error", // 不允许出现空的代码块
"@typescript-eslint/no-explicit-any": "off", // 允许any类型
"block-scoped-var": "error", // 将变量声明放在合适的代码块里
"curly": ["error", "all"], // 强制使用花括号的风格
"no-self-compare": "error", // 不允许自身比较
"no-multiple-empty-lines": ["error", { max: 2 }], // 空行最多不能超过两行
"no-const-assign": "error", // 禁止修改const声明的变量
"no-redeclare": "error", // 禁止重复声明变量
"no-func-assign": "error", // 禁止重复的函数声明
"no-shadow": "error", // 外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
// 空格
"space-infix-ops": ["error", { int32Hint: true }], // 操作符周围的空格
"space-before-function-paren": [
"error",
{ anonymous: "never", named: "never", asyncArrow: "always" },
], // 函数定义时括号前的空格
"space-before-blocks": ["error", "always"], // 在块语句之前始终有一个空格
"template-curly-spacing": ["off", "never"], // 要求模板字符串中的嵌入表达式周围空格的使用
"key-spacing": ["error", { beforeColon: false, afterColon: true }], // 对象字面量中冒号的前后空格
"array-bracket-spacing": ["off", "always"], // 数组内前后要求有空格
"object-curly-spacing": ["error", "always"], // 对象内前后要求有空格
"arrow-spacing": ["error", { before: true, after: true }], // 前头=> 前后都有空格
"comma-spacing": ["error", { before: false, after: true }], // 要求同一行内逗号后面有空格
"keyword-spacing": "error", // 关键字前后的空格
"no-trailing-spaces": "error", // 一行最后不允许有空格
"switch-colon-spacing": ["error", { before: false, after: true }], // switch 冒号后要有空格
"no-multi-spaces": "error", // 不允许出现多余的空格
/* vue相关 - https://eslint.vuejs.org */
"vue/max-attributes-per-line": [
"error",
{
singleline: { max: 3 },
multiline: { max: 1 },
},
],
"vue/html-indent": ["error", "tab"],
"vue/multi-word-component-names": "off",
"vue/html-closing-bracket-spacing": [
"error",
{
startTag: "never",
endTag: "never",
selfClosingTag: "always",
},
],
"vue/no-async-in-computed-properties": "error",
"vue/space-infix-ops": "error",
"vue/key-spacing": [
"error",
{
beforeColon: false,
afterColon: true,
},
],
"vue/no-extra-parens": ["error", "all"],
"vue/multiline-ternary": ["error", "always-multiline"],
/* ts相关 - https://eslint.style */
"@typescript-eslint/indent": "off", // 禁用ts的缩进规则
/* stylistic相关 - https://eslint.style/ */
"@stylistic/multiline-ternary": ["error", "always-multiline"],
"@stylistic/indent": [
"error",
"tab",
{
SwitchCase: 1,
offsetTernaryExpressions: true,
flatTernaryExpressions: true,
},
],
"@stylistic/member-delimiter-style": "off", // 配置对象属性的分隔符风格
"@stylistic/max-statements-per-line": ["error", { max: 2 }], // 每行最多2个语句
"@stylistic/newline-per-chained-call": [
"error",
{ ignoreChainWithDepth: 2 },
], // 多行链式调用时,每行前面必须换行
"@stylistic/arrow-parens": ["error", "as-needed"], // 箭头函数的括号能省略就省略
"@stylistic/no-tabs": ["off", { allowIndentationTabs: false }],
"@stylistic/no-mixed-spaces-and-tabs": "off",
},
},
]
// eslint-rules/eslint-plugin-vue
const { name, version } = require("../package.json")
const vueStylePrettier = require("./vue-style-prettier")
const plugin = {
meta: {
name,
version,
},
rules: {
"vue-style-prettier": vueStylePrettier, // vue模板中style使用prettier
},
}
module.exports = plugin
// eslint-rules/vue-style-prettier
/* vue模板中style使用prettier */
const synchronizedPrettier = require("@prettier/sync")
const { parse } = require("@vue/compiler-sfc")
module.exports = {
meta: {
type: "layout",
fixable: "whitespace",
messages: {
checkMessage: "样式不符合规范,应使用Prettier进行格式化",
},
schema: [], // no options
},
create(context) {
return {
Program() {
if (context.getFilename().endsWith(".vue")) {
const content = context.getSourceCode().text
const parsed = parse(content)
const styles = parsed.descriptor.styles
if (styles.length > 0) {
styles.forEach(styleBlock => {
const formatted = "\n" + synchronizedPrettier.format(
styleBlock.content,
{
parser: "scss",
singleQuote: false,
trailingComma: "all",
tabWidth: 4,
endOfLine: "auto",
arrowParens: "avoid",
printWidth: 80,
vueIndentScriptAndStyle: false,
embeddedLanguageFormatting: "off",
},
)
if (styleBlock.content !== formatted) {
context.report({
loc: {
start: styleBlock.loc.start,
end: styleBlock.loc.end,
},
messageId: "checkMessage",
fix(fixer) {
return fixer.replaceTextRange(
[
styleBlock.loc.start.offset,
styleBlock.loc.end.offset,
],
formatted,
)
},
})
}
})
}
}
},
}
},
}
总结
配置可能有重复覆盖的情况,根据项目情况自行调整
规则中大致包含:
- 缩进为1个tab(等于4个空格)
- 结尾,不可缩略,但;省略
- 正常空格都为一个
- 禁止重复声明(变量,函数等)
- if else中else另起一行
- vue中元素超过3个属性默认全部换行
- vue中的style使用prettier格式化