🔥 我与 ESLint 的爱恨纠葛:从"这破玩意儿"到"真香警告"

一个前端开发者的血泪史:如何被 ESLint 9.x + Vue3 彻底征服 💘

😤 → 😍 心路历程大公开

😤 从前的我:ESLint 黑粉

javascript 复制代码
// 我的内心 OS:
console.log("又是该死的 ESLint 报错!");
console.log("这规则也太变态了吧!");
console.log("老子就喜欢用 var,怎么了!");

黑粉时期的痛点:

  • 🤬 配置文件看着就头大
  • 😡 一堆红色波浪线,强迫症受不了
  • 🙄 规则太多,感觉在限制我的"创意"

😍 现在的我:真香现场

javascript 复制代码
// 现在的我:
console.log("ESLint 你好棒棒!✨");
console.log("自动修复爽到飞起!🚀");
console.log("团队代码整齐划一,强迫症狂喜!🎉");

真香定律触发条件:

  • 团队协作神器:再也不用吐槽队友的"艺术代码"
  • 🐛 Bug 终结者:提前发现低级错误,告别线上翻车
  • 🤖 懒人福音:保存自动格式化,爽到不想手动调整
  • 💰 加薪利器:代码质量提升,绩效蹭蹭涨(大雾)

重要提醒: 本文可能引起严重的"真香"副作用,请谨慎食用 ⚠️

🎬 手把手教学:3分钟从入门到真香

🏗️ 搭建舞台(项目初始化)

方案一:Vite 大法好 👑(强烈推荐,启动速度飞快)

bash 复制代码
npm create vue@latest my-awesome-project
cd my-awesome-project  
npm install

方案二:经典 Webpack 🏛️(老而弥坚)

bash 复制代码
vue create my-project --preset default

方案三:新贵 RSBuild 🆕(尝鲜党专用)

bash 复制代码
npm init rsbuild@latest

💊 安装"真香"套餐

一键梭哈,直接起飞:

bash 复制代码
npm install -D [email protected] prettier eslint-plugin-vue eslint-config-prettier eslint-plugin-import @eslint/js globals

这些包都是干啥的?别急,听我细说:

包名 作用 江湖地位
eslint 代码检查大佬 👑 必装,江湖老大
eslint-plugin-vue Vue3 专用规则包 🎯 Vue 项目必备
eslint-config-prettier 和谐使者 🤝 解决冲突专家
prettier 代码美容师 💄 颜值担当

💡 小贴士: 这就像组装高达模型,每个零件都有其独特作用,缺一不可!

🎨 配置大师班:让 ESLint 9.x 为你所用

🚀 一键生成配置文件:告别手工配置

ESLint 9.x 提供了官方的配置初始化工具,再也不用手动创建配置文件!

bash 复制代码
# 🎯 官方推荐方式(会自动生成 eslint.config.js)
npm init @eslint/config@latest

# 🔧 备选方案
npx eslint --init

初始化过程超简单: 跟着命令行提示,根据你的项目情况随便选择就行,反正我们后面会自己配置一套更强大的方案!

🎯 贴心提示: 初始化只是为了快速生成基础配置文件,真正的精髓在后面的自定义配置。我们会根据实际项目需求,一步步优化出更适合团队开发的 ESLint + Vue3 配置方案!

🔄 想用 .mjs 扩展名?一键搞定!

方法一:自动生成后重命名 ⭐⭐⭐⭐⭐(推荐)(只是因为个人习惯)

bash 复制代码
# 1. 先生成配置文件
npm init @eslint/config@latest

# 2. 重命名为 .mjs
mv eslint.config.js eslint.config.mjs

方法二:手动创建 .mjs 文件 ⭐⭐⭐

bash 复制代码
# 直接创建 .mjs 文件,手动配置
touch eslint.config.mjs

💡 小贴士: ESLint 会自动按以下顺序查找配置文件:

  1. eslint.config.js
  2. eslint.config.mjs
  3. eslint.config.cjs

所以重命名后就会优先使用 .mjs 文件!

🔮 ESLint 9.x 的革命性改变

还在用老式 .eslintrc.js?太 out 了!ESLint 9.x 带来了扁平化配置革命:

javascript 复制代码
// 旧时代 .eslintrc.js(已过时)❌
module.exports = {
  extends: ['@vue/standard'],
  rules: { /* 一堆嵌套配置 */ }
}

// 新时代 eslint.config.js(自动生成)✅  
export default [
  js.configs.recommended,
  // 扁平化,清晰明了!
]

为什么扁平化这么香?

  • 🧠 心智负担更低:不用再猜配置的继承关系
  • 性能更好:解析速度提升 40%
  • 🔧 调试更容易:配置问题一眼就能看出来

📁 配置文件命名规则

文件名 项目类型 推荐度
eslint.config.js CommonJS 项目 ⭐⭐⭐⭐⭐
eslint.config.mjs 任何项目 ⭐⭐⭐⭐
eslint.config.cjs ESM 项目 ⭐⭐⭐

💡 小贴士: 如果项目有 "type": "module" 就用 .js,否则用 .mjs

🛠️ 工作流集成:保存自动修复神器

💡 重要说明: 本文重点介绍 ESLint 规则配置,Prettier 配置仅作基础集成展示。后续我们会专门详细介绍 Prettier 的配置和最佳实践。

🎯 VS Code 配置:一键搞定自动修复

创建 .vscode/settings.json 文件,直接复制以下内容:

json 复制代码
{
  // 启用保存时自动格式化
  "editor.formatOnSave": true,

  // 设置默认格式化工具为 Prettier
  "editor.defaultFormatter": "esbenp.prettier-vscode",

  // ESLint 配置
  "eslint.enable": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
    "html",
    "markdown"
  ],
  "eslint.useFlatConfig": true,
  "eslint.workingDirectories": [
    {
      "mode": "auto"
    }
  ],
  "eslint.options": {
    "ignorePatterns": ["**/dist/**", "**/build/**"]
  },

  // 保存时的代码操作
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
    // "source.organizeImports": "explicit"  // 暂时禁用,使用 ESLint 管理 import
  },

  // 自动清理行尾空格
  "files.trimTrailingWhitespace": true,
  "files.trimFinalNewlines": true,
  "files.insertFinalNewline": true,

  // Prettier 扩展配置
  "prettier.requireConfig": true,
  "prettier.useEditorConfig": false
}

🎨 Prettier 配置:直接复制就行

创建 .prettierrc.mjs 文件:

javascript 复制代码
export default {
  printWidth: 100, // 每行最大字符数,超过会自动换行
  tabWidth: 2, // 缩进使用的空格数
  useTabs: false, // 使用空格而不是制表符进行缩进
  semi: true, // 语句末尾添加分号
  singleQuote: true, // 使用单引号而不是双引号
  quoteProps: 'as-needed', // 对象属性仅在需要时添加引号
  bracketSpacing: true, // 对象大括号内添加空格 { foo: bar } 而不是 {foo: bar}
  bracketSameLine: false, // HTML 标签的 > 换行显示
  arrowParens: 'avoid', // 箭头函数单个参数时省略括号 x => x 而不是 (x) => x
  endOfLine: 'lf', // 使用 Unix 换行符 (LF)
  trailingComma: 'none', // 不在任何地方添加尾随逗号
  vueIndentScriptAndStyle: false, // Vue 文件中 script 和 style 标签不额外缩进
  singleAttributePerLine: false, // HTML 属性不强制每行一个
  htmlWhitespaceSensitivity: 'css', // HTML 空格敏感度,遵循 CSS 规则
  embeddedLanguageFormatting: 'auto' // 自动格式化嵌入的代码块(如 Vue 模板中的 JS)
};

🚀 配置完成后的效果: 每次 Ctrl+S 保存时,ESLint 会自动修复代码问题,Prettier 会自动格式化代码样式,爽到飞起!

🎯 ESLint 配置实战:先来个简单的

现在来配置一个最基础的 eslint.config.mjs,先测试一下自动修复功能:

javascript 复制代码
// eslint.config.mjs
import configPrettier from 'eslint-config-prettier';

export default [
  {
    files: ['**/*.{js,mjs,cjs,vue}'],
    rules: {
      // 未使用的变量报错
      'no-unused-vars': 'error'
    }
  },
  {
    // Prettier 配置(必须放在最后)
    // Prettier 集成,解决 ESLint 和 Prettier 的冲突
    files: ['**/*.{js,mjs,jsx,ts,tsx,vue}'],
    ...configPrettier
  }
];

💡 测试小贴士:

  1. 写一个未使用的变量,保存时会看到红色波浪线
  2. 写一些格式不规范的代码,保存时会自动格式化
  3. 这就是 ESLint 自动修复的魅力!

🚀 Import 插件:模块导入的艺术大师

重头戏来了! eslint-plugin-import 是前端项目中最重要的插件之一,专门负责规范化模块的导入/导出。

🎯 为什么这么重要?

javascript 复制代码
// 😵 混乱的导入(没有规范)
import { computed, ref } from 'vue';
import axios from 'axios';
import { Button } from '@/components';
import lodash from 'lodash';
import './App.vue';
import { ElMessage } from 'element-plus';
import { utils } from '../utils/helper';
import fs from 'fs';

// 😍 整洁的导入(Import 插件规范化后)
import fs from 'fs';

import axios from 'axios';
import { ElMessage } from 'element-plus';
import lodash from 'lodash';
import { computed, ref } from 'vue';

import { Button } from '@/components';

import { utils } from '../utils/helper';

import './App.vue';

🛠️ 快速配置

bash 复制代码
npm install -D eslint-plugin-import
javascript 复制代码
// eslint.config.mjs
import pluginImport from 'eslint-plugin-import';

export default [
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    plugins: {
      import: pluginImport
    },
    rules: {
      // 导入后强制换行
      'import/newline-after-import': 'error',
      
      // 防止重复导入同一模块
      'import/no-duplicates': 'error',
      
      // 导入排序规则(核心功能)
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
          'newlines-between': 'always',
          alphabetize: { order: 'asc', caseInsensitive: true }
        }
      ],
      
      // import 语句内部成员排序
      'sort-imports': [
        'error',
        {
          ignoreCase: true,
          ignoreDeclarationSort: true,
          ignoreMemberSort: false
        }
      ]
    }
  }
];

🚀 核心价值

  • 🎯 统一导入风格:按类型自动分组排序(内置模块 → npm包 → 内部模块 → 样式文件)
  • 🔍 依赖关系清晰:一眼看出项目的依赖结构
  • 🧹 自动化处理:保存时自动整理,无需手动调整
  • 💰 减少争议:告别 Code Review 中的导入顺序讨论

📚 更多配置选项: 具体的 pathGroups、自定义别名等高级配置请查阅 eslint-plugin-import 官方文档

🛠️ 先介绍通用配置和第三方插件

在深入具体规则之前,我们先了解一些通用的配置选项和强大的第三方扩展插件。

📂 ignores - 忽略不需要检查的文件

ignores 配置让 ESLint 跳过某些不需要检查的文件和目录,避免浪费时间检查第三方代码或自动生成的文件。

javascript 复制代码
// eslint.config.mjs
export default [
  // 忽略配置(建议放在最前面)
  {
    ignores: [
      // 依赖目录
      'node_modules/**',
      '**/node_modules/**',
      
      // 构建产物
      'dist/**',
      '**/dist/**',
      
      // 更多忽略项...
    ]
  },
  
  // 其他配置...
];

💡 为什么要忽略这些?

  • node_modules/**:第三方依赖,不是我们的代码
  • dist/**:构建后的文件,通常是压缩过的,检查没意义

⭐ js.configs.recommended - ESLint 官方推荐配置

这是 ESLint 官方精心挑选的一组规则,包含了最重要的代码质量和错误检测规则。

javascript 复制代码
// eslint.config.mjs
import js from '@eslint/js';

export default [
  // 应用 ESLint 官方推荐配置
  js.configs.recommended,
  
  // 你的其他配置...
];

🎯 官方推荐配置包含什么?

  • 语法错误检测(如未定义变量、重复声明等)
  • 常见错误预防(如条件语句中的赋值)
  • 代码质量提升(如未使用的变量警告)

🎯 JavaScript/TypeScript 文件配置

为 JS/TS 文件设置语言选项和运行环境,告诉 ESLint 如何解析和检查这些文件。

javascript 复制代码
// eslint.config.mjs
import globals from 'globals';

export default [
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    languageOptions: {
      ecmaVersion: 'latest',        // 使用最新的 ECMAScript 版本
      sourceType: 'module',         // 代码使用 ES6 模块
      globals: {
        ...globals.browser,         // 浏览器环境全局变量(如 window、document)
        ...globals.node,           // Node.js 环境全局变量(如 process、__dirname)
        ...globals.es2020          // ES2020 全局变量
      }
    }
  }
];

📚 配置解释:

  • files:指定这个配置应用于哪些文件
  • ecmaVersion: 'latest':支持最新的 JavaScript 语法
  • sourceType: 'module':支持 import/export 语法
  • globals:定义全局变量,避免"未定义变量"的误报

🔥 ESLint 规则详解:从零到精通

接下来我介绍的全都是 ESLint 的 rules,并且会有详细的每一条规则的正例反例,让你彻底搞懂每个规则的作用和使用场景!

📚 学习方式建议: 每个规则都会包含:

  • 🎯 规则说明:这个规则是干什么的
  • 反例代码:什么样的代码会被报错
  • 正例代码:怎么写才是正确的
  • 💡 实际场景:在什么情况下这个规则特别有用

代码质量规则

no-var - 禁用 var 声明

javascript 复制代码
'no-var': 'error'

强制使用 letconst 替代 var,避免变量提升和作用域问题。

反例:

javascript 复制代码
var name = '张三';
if (true) {
  var message = 'Hello';
}
console.log(message); // 能访问到,这是问题

正例:

javascript 复制代码
const name = '张三';
let age = 25;
if (true) {
  const message = 'Hello';
}
// console.log(message); // 报错,正确的块级作用域

object-shorthand - 对象简写语法

javascript 复制代码
'object-shorthand': 'error'

在对象中使用 ES6 简写语法。

反例:

javascript 复制代码
const name = '张三';
const user = {
  name: name,
  getName: function() {
    return this.name;
  }
};

正例:

javascript 复制代码
const name = '张三';
const user = {
  name,
  getName() {
    return this.name;
  }
};

prefer-const - 优先使用 const

javascript 复制代码
'prefer-const': 'error'

如果变量不会被重新赋值,使用 const

反例:

javascript 复制代码
let userName = 'admin'; // 后面没有重新赋值
let config = { theme: 'dark' };

正例:

javascript 复制代码
const userName = 'admin';
const config = { theme: 'dark' };
let currentTheme = 'dark'; // 这个会改变,所以用 let
currentTheme = 'light';

prefer-template - 优先使用模板字符串

javascript 复制代码
'prefer-template': 'error'

使用模板字符串替代字符串拼接。

反例:

javascript 复制代码
const name = '张三';
const greeting = 'Hello, ' + name + '!';
const info = '姓名:' + name + ',年龄:' + age + '岁';

正例:

javascript 复制代码
const name = '张三';
const greeting = `Hello, ${name}!`;
const info = `姓名:${name},年龄:${age}岁`;

安全规则

no-caller - 禁用 arguments.caller 或 arguments.callee

javascript 复制代码
'no-caller': 'error'

禁止使用 arguments.callerarguments.callee,这些在严格模式下会报错。

反例:

javascript 复制代码
function foo() {
  var callee = arguments.callee;
  var caller = arguments.caller;
}

正例:

javascript 复制代码
function foo() {
  // 使用函数名或其他方式
}

no-eval - 禁用 eval()

javascript 复制代码
'no-eval': 'error'

禁止使用 eval(),存在安全风险。

反例:

javascript 复制代码
var result = eval('2 + 2');
var func = eval('function() { return 1; }');

正例:

javascript 复制代码
var result = 2 + 2;
var func = function() { return 1; };

no-extend-native - 禁止扩展原生类型

javascript 复制代码
'no-extend-native': 'error'

禁止扩展原生对象的原型。

反例:

javascript 复制代码
Array.prototype.customMethod = function() {};
String.prototype.isBlank = function() {};

正例:

javascript 复制代码
// 使用工具函数
function customMethod(arr) {}
function isBlank(str) {}

no-implied-eval - 禁止使用隐式 eval()

javascript 复制代码
'no-implied-eval': 'error'

禁止使用隐式的 eval(),如 setTimeout 中的字符串代码。

反例:

javascript 复制代码
setTimeout('alert("Hello");', 1000);
setInterval('console.log("test")', 1000);

正例:

javascript 复制代码
setTimeout(function() { alert("Hello"); }, 1000);
setInterval(() => console.log("test"), 1000);

no-new-func - 禁用 Function 构造函数

javascript 复制代码
'no-new-func': 'error'

禁止使用 new Function() 创建函数,类似 eval()

反例:

javascript 复制代码
var fn = new Function('a', 'b', 'return a + b');
var fn2 = Function('return "test"');

正例:

javascript 复制代码
var fn = function(a, b) { return a + b; };
var fn2 = () => "test";

no-proto - 禁用 proto 属性

javascript 复制代码
'no-proto': 'error'

禁止使用 __proto__ 属性,使用 Object.getPrototypeOf 替代。

反例:

javascript 复制代码
var proto = obj.__proto__;
obj.__proto__ = newProto;

正例:

javascript 复制代码
var proto = Object.getPrototypeOf(obj);
Object.setPrototypeOf(obj, newProto);

no-return-assign - 禁止在 return 语句中使用赋值语句

javascript 复制代码
'no-return-assign': 'error'

禁止在 return 语句中进行赋值操作。

反例:

javascript 复制代码
function doSomething() {
  return result = a + b;
}

正例:

javascript 复制代码
function doSomething() {
  result = a + b;
  return result;
}

no-script-url - 禁止使用 javascript: url

javascript 复制代码
'no-script-url': 'error'

禁止使用 javascript: 协议的 URL。

反例:

javascript 复制代码
var link = "javascript:void(0)";
location.href = "javascript:void(0)";

正例:

javascript 复制代码
var link = "#";
// 使用事件处理函数
element.addEventListener('click', handler);

no-throw-literal - 禁止抛出字面量错误

javascript 复制代码
'no-throw-literal': 'error'

禁止抛出字面量作为异常,应该抛出 Error 对象。

反例:

javascript 复制代码
throw "error";
throw 404;
throw true;

正例:

javascript 复制代码
throw new Error("error");
throw new TypeError("Expected a string");

代码逻辑规则

default-case - 要求 switch 语句中有 default 分支

javascript 复制代码
'default-case': 'warn'

要求 switch 语句包含 default 分支。

反例:

javascript 复制代码
switch (type) {
  case 'A':
    handleA();
    break;
  case 'B':
    handleB();
    break;
  // 缺少 default 分支
}

正例:

javascript 复制代码
switch (type) {
  case 'A':
    handleA();
    break;
  case 'B':
    handleB();
    break;
  default:
    handleDefault();
}

no-empty - 禁止出现空语句块

javascript 复制代码
'no-empty': ['error', { allowEmptyCatch: false }]

禁止出现空的语句块。

反例:

javascript 复制代码
if (condition) {
  // 空的代码块
}

try {
  doSomething();
} catch (e) {
  // 空的 catch 块
}

正例:

javascript 复制代码
if (condition) {
  doSomething();
}

try {
  doSomething();
} catch (e) {
  console.error(e);
}

no-empty-function - 禁止出现空函数

javascript 复制代码
'no-empty-function': 'warn'

禁止出现空的函数声明。

反例:

javascript 复制代码
function emptyFunction() {
  // 空函数
}

const emptyArrow = () => {
  // 空函数
};

正例:

javascript 复制代码
function validFunction() {
  return true;
}

const validArrow = () => {
  // TODO: 实现逻辑
};

no-fallthrough - 禁止 case 语句落空

javascript 复制代码
'no-fallthrough': 'error'

禁止 switch 语句中的 case 穿透到下一个 case。

反例:

javascript 复制代码
switch (type) {
  case 'A':
    handleA();
    // 缺少 break,会穿透到下一个 case
  case 'B':
    handleB();
    break;
}

正例:

javascript 复制代码
switch (type) {
  case 'A':
    handleA();
    break;
  case 'B':
    handleB();
    break;
}

no-unreachable - 禁止不可达代码

javascript 复制代码
'no-unreachable': 'error'

禁止在 return、throw、continue 和 break 语句之后出现不可达代码。

反例:

javascript 复制代码
function example() {
  return true;
  console.log('这行代码永远不会执行'); // 不可达代码
}

正例:

javascript 复制代码
function example() {
  if (condition) {
    return true;
  }
  console.log('这行代码可以执行');
}

no-unused-expressions - 禁用未使用的表达式

javascript 复制代码
'no-unused-expressions': 'error'

禁止使用未使用的表达式。

反例:

javascript 复制代码
2 + 2; // 未使用的表达式
'hello'; // 未使用的字符串
a && b; // 未使用的逻辑表达式

正例:

javascript 复制代码
const result = 2 + 2;
console.log('hello');
if (a && b) {
  doSomething();
}

命名约定规则

camelcase - 强制使用驼峰拼写法

javascript 复制代码
camelcase: ['error', { properties: 'never', ignoreDestructuring: true }]

强制使用驼峰命名法,但忽略属性名和解构。

反例:

javascript 复制代码
var first_name = 'John';
function get_user_info() {}

正例:

javascript 复制代码
var firstName = 'John';
function getUserInfo() {}
// 对象属性和解构可以使用下划线
const { user_id } = data;

new-cap - 要求构造函数首字母大写

javascript 复制代码
'new-cap': ['error', { newIsCap: true, capIsNew: false }]

要求构造函数使用大写开头,new 操作符只用于大写开头的函数。

反例:

javascript 复制代码
var person = new person();
var Person = person();

正例:

javascript 复制代码
var person = new Person();
var result = person();

no-underscore-dangle - 限制标识符中悬空下划线

javascript 复制代码
'no-underscore-dangle': ['warn', { allowAfterThis: true, allow: ['_id', '__dirname', '__filename'] }]

限制在标识符中使用悬空下划线,但允许特定情况。

反例:

javascript 复制代码
var _private = 'secret';
var ending_ = 'bad';

正例:

javascript 复制代码
var private = 'secret';
var ending = 'good';
this._property = 'allowed';
var _id = 'allowed';

性能优化规则

no-array-constructor - 禁用 Array 构造函数

javascript 复制代码
'no-array-constructor': 'error'

禁止使用 Array 构造函数创建数组。

反例:

javascript 复制代码
var arr = new Array(1, 2, 3);
var emptyArr = new Array(10);

正例:

javascript 复制代码
var arr = [1, 2, 3];
var emptyArr = Array(10);
var filled = Array.from({ length: 10 });

no-loop-func - 禁止在循环中出现函数声明和表达式

javascript 复制代码
'no-loop-func': 'error'

禁止在循环语句中出现包含不安全引用的函数声明或表达式。

反例:

javascript 复制代码
for (var i = 0; i < 10; i++) {
  funcs[i] = function() {
    return i; // 闭包问题
  };
}

正例:

javascript 复制代码
for (let i = 0; i < 10; i++) {
  funcs[i] = function() {
    return i; // let 解决闭包问题
  };
}

no-new-object - 禁用 Object 的构造函数

javascript 复制代码
'no-new-object': 'error'

禁止使用 Object 构造函数。

反例:

javascript 复制代码
var obj = new Object();
var obj2 = new Object(1);

正例:

javascript 复制代码
var obj = {};
var obj2 = Object(1);

no-new-wrappers - 禁止对 String,Number 和 Boolean 使用 new 操作符

javascript 复制代码
'no-new-wrappers': 'error'

禁止使用原始包装器的构造函数。

反例:

javascript 复制代码
var str = new String('hello');
var num = new Number(42);
var bool = new Boolean(true);

正例:

javascript 复制代码
var str = String('hello');
var num = Number(42);
var bool = Boolean(true);

代码维护性规则

complexity - 限制圈复杂度

javascript 复制代码
complexity: ['warn', 10]

限制函数的圈复杂度不超过 10。

反例:

javascript 复制代码
function complex(x) {
  if (x > 10) {
    if (x > 20) {
      if (x > 30) {
        // 太多嵌套的条件
      }
    }
  }
}

正例:

javascript 复制代码
function simple(x) {
  if (x <= 10) return;
  if (x <= 20) return;
  // 减少嵌套层次
}

max-depth - 强制可嵌套的块的最大深度

javascript 复制代码
'max-depth': ['warn', 4]

强制块语句的最大可嵌套深度为 4。

反例:

javascript 复制代码
if (true) {
  if (true) {
    if (true) {
      if (true) {
        if (true) { // 超过 4 层嵌套
        }
      }
    }
  }
}

正例:

javascript 复制代码
if (condition1 && condition2 && condition3) {
  // 减少嵌套层次
}

max-lines-per-function - 强制函数的最大行数

javascript 复制代码
'max-lines-per-function': ['warn', 50]

强制函数的最大行数不超过 50 行。

反例:

javascript 复制代码
function tooLong() {
  // 超过 50 行的函数
  // ...
}

正例:

javascript 复制代码
function reasonableSize() {
  // 少于 50 行的函数
}

max-params - 强制函数定义中的最大参数个数

javascript 复制代码
'max-params': ['warn', 4]

强制函数定义中的最大参数个数为 4。

反例:

javascript 复制代码
function tooManyParams(a, b, c, d, e) {
  // 超过 4 个参数
}

正例:

javascript 复制代码
function goodParams(a, b, c, d) {
  // 4 个或更少参数
}

function withOptions(config) {
  // 使用对象参数
}

基础格式化规则

prettier/prettier - Prettier 格式化

javascript 复制代码
'prettier/prettier': 'error'

使用 Prettier 进行代码格式化。

semi - 强制使用分号

javascript 复制代码
semi: ['error', 'always']

强制在语句末尾使用分号。

反例:

javascript 复制代码
var name = 'John'
console.log('Hello')

正例:

javascript 复制代码
var name = 'John';
console.log('Hello');

Vue 基础规则

vue/component-name-in-template-casing - 组件名称大小写

javascript 复制代码
'vue/component-name-in-template-casing': ['error', 'PascalCase']

强制模板中组件名使用 PascalCase。

反例:

vue 复制代码
<template>
  <user-profile />
  <userProfile />
</template>

正例:

vue 复制代码
<template>
  <UserProfile />
</template>

vue/multi-word-component-names - 多词组件名

javascript 复制代码
'vue/multi-word-component-names': 'off'

要求组件名称始终为多个单词(此规则已关闭)。

vue/no-v-html - 禁止使用 v-html

javascript 复制代码
'vue/no-v-html': 'warn'

禁止使用 v-html 指令以防止 XSS 攻击。

反例:

vue 复制代码
<div v-html="userContent"></div>

正例:

vue 复制代码
<div>{{ userContent }}</div>

vue/require-explicit-emits - 要求显式声明 emits

javascript 复制代码
'vue/require-explicit-emits': 'error'

要求显式声明组件的 emits。

反例:

vue 复制代码
<script setup>
// 没有声明 emits
const emit = defineEmits();
emit('update');
</script>

正例:

vue 复制代码
<script setup>
const emit = defineEmits(['update']);
emit('update');
</script>

Vue 属性和命名规则

vue/attribute-hyphenation - 属性名连字符

javascript 复制代码
'vue/attribute-hyphenation': ['error', 'always']

强制属性名使用连字符形式。

反例:

vue 复制代码
<MyComponent userName="John" />

正例:

vue 复制代码
<MyComponent user-name="John" />

vue/custom-event-name-casing - 自定义事件命名

javascript 复制代码
'vue/custom-event-name-casing': ['error', 'kebab-case']

强制自定义事件名使用 kebab-case。

反例:

vue 复制代码
<script setup>
const emit = defineEmits(['updateUser']);
</script>

正例:

vue 复制代码
<script setup>
const emit = defineEmits(['update-user']);
</script>

vue/prop-name-casing - Props 命名

javascript 复制代码
'vue/prop-name-casing': ['error', 'camelCase']

强制 prop 名称使用 camelCase。

反例:

vue 复制代码
<script setup>
defineProps({
  'user-name': String
});
</script>

正例:

vue 复制代码
<script setup>
defineProps({
  userName: String
});
</script>

Vue Composition API 规则

vue/define-emits-declaration - defineEmits 声明

javascript 复制代码
'vue/define-emits-declaration': ['error', 'type-literal']

强制 defineEmits 使用类型字面量声明。

反例:

vue 复制代码
<script setup>
const emit = defineEmits(['update']);
</script>

正例:

vue 复制代码
<script setup>
const emit = defineEmits<{
  update: [value: string]
}>();
</script>

vue/define-props-declaration - defineProps 声明

javascript 复制代码
'vue/define-props-declaration': ['error', 'type-based']

强制 defineProps 使用基于类型的声明。

反例:

vue 复制代码
<script setup>
const props = defineProps({
  name: String
});
</script>

正例:

vue 复制代码
<script setup>
interface Props {
  name: string;
}
const props = defineProps<Props>();
</script>

vue/no-ref-as-operand - 禁止将 ref 作为操作数

javascript 复制代码
'vue/no-ref-as-operand': 'error'

禁止直接使用 ref 作为操作数。

反例:

vue 复制代码
<script setup>
const count = ref(0);
if (count > 0) {} // 错误:应该使用 count.value
</script>

正例:

vue 复制代码
<script setup>
const count = ref(0);
if (count.value > 0) {}
</script>

Vue 最佳实践规则

vue/no-empty-component-block - 禁止空组件块

javascript 复制代码
'vue/no-empty-component-block': 'error'

禁止空的组件块。

反例:

vue 复制代码
<template></template>
<script></script>
<style></style>

正例:

vue 复制代码
<template>
  <div>Content</div>
</template>

vue/no-useless-mustaches - 禁止无用的插值

javascript 复制代码
'vue/no-useless-mustaches': 'error'

禁止不必要的插值表达式。

反例:

vue 复制代码
<template>
  <div>{{ 'static text' }}</div>
</template>

正例:

vue 复制代码
<template>
  <div>static text</div>
</template>

vue/no-useless-v-bind - 禁止无用的 v-bind

javascript 复制代码
'vue/no-useless-v-bind': 'error'

禁止不必要的 v-bind 指令。

反例:

vue 复制代码
<template>
  <div v-bind:id="'static'"></div>
</template>

正例:

vue 复制代码
<template>
  <div id="static"></div>
</template>

Vue 文件专用配置

Vue 插件推荐配置

javascript 复制代码
...pluginVue.configs['flat/recommended'].map(config => ({
  ...config,
  files: ['**/*.vue']
}))

应用 Vue 插件的推荐配置到所有 Vue 文件。

这个配置会自动启用所有 Vue 推荐的规则,包括:

  • Vue 3 语法检查
  • 模板语法验证
  • 组件结构规范
  • 响应式数据使用规范

使用示例:

javascript 复制代码
// eslint.config.mjs
import pluginVue from 'eslint-plugin-vue';

export default [
  // 其他配置...
  
  // Vue 文件专用配置
  ...pluginVue.configs['flat/recommended'].map(config => ({
    ...config,
    files: ['**/*.vue']
  }))
];

最后给大家一个全的 eslint.config.mjs

javascript 复制代码
import js from '@eslint/js';
import configPrettier from 'eslint-config-prettier';
import pluginImport from 'eslint-plugin-import';
import pluginPrettier from 'eslint-plugin-prettier';
import pluginVue from 'eslint-plugin-vue';
import globals from 'globals';

export default [
  {
    ignores: [
      // 依赖目录
      'node_modules/**',
      '**/node_modules/**',

      // 构建产物
      'dist/**',
      '**/dist/**'

      // 更多忽略项...
    ]
  },
  js.configs.recommended,
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    languageOptions: {
      ecmaVersion: 'latest', // 使用最新的 ECMAScript 版本
      sourceType: 'module', // 代码使用 ES6 模块
      globals: {
        ...globals.browser, // 浏览器环境全局变量(如 window、document)
        ...globals.node, // Node.js 环境全局变量(如 process、__dirname)
        ...globals.es2020 // ES2020 全局变量
      }
    },
    plugins: {
      import: pluginImport
    },
    rules: {
      // 导入后强制换行
      'import/newline-after-import': 'error',

      // 防止重复导入同一模块
      'import/no-duplicates': 'error',

      // 导入排序规则(核心功能)
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
          'newlines-between': 'always',
          alphabetize: { order: 'asc', caseInsensitive: true }
        }
      ],

      // import 语句内部成员排序
      'sort-imports': [
        'error',
        {
          ignoreCase: true,
          ignoreDeclarationSort: true,
          ignoreMemberSort: false
        }
      ],
      // ===== 代码质量规则 =====
      'no-var': 'error',
      'object-shorthand': 'error',
      'prefer-const': 'error',
      'prefer-template': 'error',
      // ===== 安全性规则 =====
      'no-caller': 'error',
      'no-eval': 'error',
      'no-extend-native': 'error',
      'no-implied-eval': 'error',
      'no-new-func': 'error',
      'no-proto': 'error',
      'no-return-assign': 'error',
      'no-script-url': 'error',
      'no-throw-literal': 'error',
      // ===== 错误处理规则 =====
      'default-case': 'warn',
      'no-empty': ['error', { allowEmptyCatch: false }],
      'no-empty-function': 'warn',
      'no-fallthrough': 'error',
      'no-unreachable': 'error',
      'no-unused-expressions': 'error',
      // ===== 命名约定规则 =====
      camelcase: ['error', { properties: 'never', ignoreDestructuring: true }],
      'new-cap': ['error', { newIsCap: true, capIsNew: false }],
      'no-underscore-dangle': [
        'warn',
        { allowAfterThis: true, allow: ['_id', '__dirname', '__filename'] }
      ],
      // ===== 性能优化规则 =====
      'no-array-constructor': 'error',
      'no-loop-func': 'error',
      'no-new-object': 'error',
      'no-new-wrappers': 'error',
      // ===== 代码维护性规则 =====
      complexity: ['warn', 10],
      'max-depth': ['warn', 4],
      'max-lines-per-function': ['warn', 50],
      'max-params': ['warn', 4]
    }
  },
  // ===================================
  // Vue 项目配置
  // ===================================
  {
    files: ['**/*.{js,mjs,jsx,vue}'],
    languageOptions: {
      globals: globals.browser
    },
    plugins: {
      import: pluginImport,
      prettier: pluginPrettier,
      vue: pluginVue
    },
    rules: {
      // ===== 基础格式化规则 =====
      'prettier/prettier': 'error',
      semi: ['error', 'always'],

      // ===== Vue 基础规则 =====
      'vue/component-name-in-template-casing': ['error', 'PascalCase'],
      'vue/multi-word-component-names': 'off',
      'vue/no-setup-props-destructure': 'off',
      'vue/no-v-html': 'warn',
      'vue/require-default-prop': 'off',
      'vue/require-explicit-emits': 'error',

      // ===== Vue 属性和命名规则 =====
      'vue/attribute-hyphenation': ['error', 'always'],
      'vue/custom-event-name-casing': ['error', 'kebab-case'],
      'vue/prop-name-casing': ['error', 'camelCase'],

      // ===== Vue Composition API 规则 =====
      'vue/define-emits-declaration': ['error', 'type-literal'],
      'vue/define-props-declaration': ['error', 'type-based'],
      'vue/no-boolean-default': ['error', 'default-false'],
      'vue/no-ref-as-operand': 'error',
      'vue/no-watch-after-await': 'error',
      'vue/prefer-define-options': 'error',
      'vue/require-macro-variable-name': 'error',

      // ===== Vue 最佳实践规则 =====
      'vue/no-duplicate-attr-inheritance': 'error',
      'vue/no-empty-component-block': 'error',
      'vue/no-multiple-objects-in-class': 'error',
      'vue/no-static-inline-styles': 'warn',
      'vue/no-useless-mustaches': 'error',
      'vue/no-useless-v-bind': 'error',
      'vue/prefer-separate-static-class': 'error'
    }
  },
  // ===================================
  // Vue 文件专用配置
  // ===================================
  ...pluginVue.configs['flat/recommended'].map(config => ({
    ...config,
    files: ['**/*.vue']
  })),
  {
    // Prettier 集成,解决 ESLint 和 Prettier 的冲突
    files: ['**/*.{js,mjs,jsx,ts,tsx,vue}'],
    ...configPrettier
  }
];

感谢大家的耐心阅读!良好的编码规范不仅能提升代码质量,更能在潜移默化中塑造我们的专业素养。在规范的环境中工作,我们会自然而然地追求更高的标准。毕竟,每一位有追求的开发者都希望自己的代码优雅而专业。工作虽是谋生手段,但对技术的执着与热爱才是我们前行的动力。

接下来我将继续分享 Prettier、Stylelint、Husky、lint-staged 等前端工程化工具的配置与最佳实践。

有点标题党了,规则并无绝对的优劣之分,关键在于是否契合团队和自己的实际需求。希望大家都能找到自己的 "lint"。

相关推荐
coding随想6 分钟前
JavaScript ES6 解构:优雅提取数据的艺术
前端·javascript·es6
小小小小宇10 分钟前
一个小小的柯里化函数
前端
灵感__idea15 分钟前
JavaScript高级程序设计(第5版):无处不在的集合
前端·javascript·程序员
小小小小宇18 分钟前
前端双Token机制无感刷新
前端
小小小小宇20 分钟前
重提React闭包陷阱
前端
小小小小宇36 分钟前
前端XSS和CSRF以及CSP
前端
UFIT40 分钟前
NoSQL之redis哨兵
java·前端·算法
超级土豆粉1 小时前
CSS3 的特性
前端·css·css3
星辰引路-Lefan1 小时前
深入理解React Hooks的原理与实践
前端·javascript·react.js
wyn200011281 小时前
JavaWeb的一些基础技术
前端