🔥 我与 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 eslint@9.x 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"。

相关推荐
gnip16 分钟前
链式调用和延迟执行
前端·javascript
SoaringHeart27 分钟前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.30 分钟前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu40 分钟前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss40 分钟前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师43 分钟前
React面试题
前端·javascript·react.js
木兮xg44 分钟前
react基础篇
前端·react.js·前端框架
ssshooter1 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘2 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai2 小时前
HTML HTML基础(4)
前端·html