一个前端开发者的血泪史:如何被 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 会自动按以下顺序查找配置文件:
eslint.config.js
eslint.config.mjs
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
}
];
💡 测试小贴士:
- 写一个未使用的变量,保存时会看到红色波浪线
- 写一些格式不规范的代码,保存时会自动格式化
- 这就是 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'
强制使用 let
或 const
替代 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.caller
或 arguments.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"。