react/react-in-jsx-scope当使用 JSX 语法时React 必须在作用域内即需要显式导入 React。在 React 17 之前每次使用 JSX 语法时Babel 会将其转换为 React.createElement() 调用因此必须导入 React 才能使用 JSX。举例index.jsxexport default () divNew Page index/div;应修改为import React from react; export default () divNew Page index/div;max-leneslint默认最大行长度为100处理方法修改eslint配置项.eslintrc.jsconst { strictEslint } require(umijs/fabric); module.exports { ...strictEslint, globals: { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, page: true, }, rules: { ...strictEslint.rules, max-len: [error, { code: 100, // 最大行长度 ignoreComments: true, // 忽略注释行 ignoreStrings: true, // 忽略字符串字面量避免因长 URL 或模板字符串报错 ignoreTemplateLiterals: true, // 忽略模板字符串 ignoreRegExpLiterals: true, // 忽略正则表达式 ignoreUrls: true, // 忽略 URL }], }, }注意如果注释行为JSX注释将不被eslint视为注释行在 ESLint 的 max-len 规则中 ignoreComments: true 只适用于 真正的注释 // 单行注释/* 多行注释 */而 JSX 中的 {/* ... */} 虽然在语义上是注释但在 AST抽象语法树中它被解析为{ - JSX 表达式开始/* ... */ - 内部的注释} - JSX 表达式结束因此ESLint 的 max-len 规则 不将 JSX 注释视为纯注释行 仍然会检查其长度。举例const SubmitFormItem ({ className, ...rest }) { const clsString classNames(styles.submit, className); return ( FormItem {/* Button sizelarge className{clsString} typeprimary htmlTypesubmit {...rest} / */} Button sizelarge className{clsString} htmlTypesubmit {...rest} / /FormItem ); };template literalsESLint 推荐使用模板字符串template literals而不是字符串拼接。const title{添加${title}} // 模态框标题Parsing error: Expression expected.const page service?.data?.page || {};在老旧项目中可选链操作符(?. )是ES2020的特性可能ESLint的解析器配置没有正确支持这个语法。修改配置项添加ES2020的特性支持.eslintrc.jsconst { strictEslint } require(umijs/fabric); module.exports { ...strictEslint, rules: { ... }, overrides: [ { files: [*.js, *.jsx], parser: babel-eslint, parserOptions: { ecmaVersion: 2020, ecmaFeatures: { jsx: true, }, }, }, ], };这样当执行npm run lint命令时就不会再因为可选链操作符而报错了变量 is defined but never used方法1使用ESLint自动修复# 自动修复所有可修复的问题包括未使用的变量 npx eslint --fix src/ # 或者使用 yarn yarn eslint --fix src/ # 或者 npm npm run lint:fix注意以下未使用对象需手动删除ESLint 无法确定是否真的不需要未使用的导入未使用的函数变量解决方案手动删除、使用代码清理工具方法2使用代码清理工具eslint-plugin-unused-imports 专门用于检测和移除未使用的导入安装插件# 使用 npm npm install --save-dev eslint-plugin-unused-imports typescript-eslint/eslint-plugin typescript-eslint/parser # 或使用 yarn yarn add --dev eslint-plugin-unused-imports typescript-eslint/eslint-plugin typescript-eslint/parser配置.eslintrc.jsmodule.exports { parser: typescript-eslint/parser, plugins: [ typescript-eslint, unused-imports // 添加插件 ], rules: { // 配置未使用导入的规则 unused-imports/no-unused-imports: error, unused-imports/no-unused-vars: [ error, { vars: all, varsIgnorePattern: ^_, args: after-used, argsIgnorePattern: ^_ } ], // 其他规则... } };运行以下命令来自动修复未使用的导入# 自动修复所有可修复的问题 npx eslint --fix src/ # 或使用 yarn yarn eslint --fix src/集成到开发流程可以将 ESLint 检查添加到项目的 package.json 脚本中scripts: { lint: eslint src/, lint:fix: eslint --fix src/ }方法3修改ESLint配置不推荐可能导致代码质量下降配置规则为警告而非错误rules: { ...strictEslint.rules, eqeqeq: off, no-unused-vars: off, typescript-eslint/no-unused-vars: warn, }或者允许忽略特定模式的未使用变量module.exports { ...strictEslint, rules: { ...strictEslint.rules, typescript-eslint/no-unused-vars: [warn, { vars: all, varsIgnorePattern: ^_|^React$, // 忽略 React 和以 _ 开头的变量 args: after-used, argsIgnorePattern: ^_ }], }, };^_ - 忽略以 _ 开头的变量如 _unused ^React$ - 忽略 React因为 JSX 需要Unary operator used意思是禁止使用自增运算符 例如 i 或 i1. 为什么会有这个规则这条规则通常出现在追求函数式编程风格或代码绝对清晰的项目中如 Airbnb 风格指南。主要理由包括隐式副作用i不仅返回值还修改变量i。在某些复杂的表达式中如array[i] i这可能导致代码难以阅读且容易出错。自动分号插入问题在某些极端情况下紧跟在行尾可能会引起解析歧义虽然现代工具处理得很好但这是一个历史遗留顾虑。鼓励显式操作强制开发者写出更明确的意图比如i 1。2. 如何解决配置.eslintrc.jsno-plusplus: off,或者// 显式加 1 i 1; // 在循环中 for (let i 0; i len; i 1) { // ... }no-nested-ternary不允许嵌套多个三元运算符代码阅读不友好可以改为if elseno-underscore-dangle在代码中使用了以下划线 _ 开头或结尾的标识符variable/property name这违反了 ESLint 的 no-underscore-dangle 规则。no-constant-condition禁止在控制流语句如if、while、for、条件运算符?:中使用常量表达式作为条件。// 1. 典型的错误写法 if (a 5) { // 报错这是一个赋值表达式结果永远是 5 (truthy)意图可能是 a 5 // ... } // 2. 逻辑运算结果恒定 if (foo || true) { // 报错无论 foo 是什么结果永远为真 // ... } // 3. 三元运算符中的常量 var a true ? foo : bar; // 报错条件恒定为真bar 永远无法执行为什么要有这条规则防止死循环while (true)如果没有内部的break会导致程序卡死。虽然有时故意写while(true)但 ESLint 希望你显式地关闭该规则或使用注释说明。发现拼写错误最常见的是把比较运算符或误写成了赋值运算符。例如if (x 5)实际上是将 5 赋值给 x然后判断 5真这通常不是开发者的本意。移除死代码如果条件是if (false)那么里面的代码永远不会执行这是无用代码。array-callback-returnmap() 方法期望回调函数 返回一个值 来构成新数组但这里的回调函数没有 return 语句将map修改为forEachno-shadow变量遮蔽禁止变量声明遮蔽Shadowing外部作用域中已存在的变量。变量遮蔽当一个内部作用域如函数内部、代码块内部声明了一个与外部作用域同名的变量时内部变量就会“遮蔽”外部变量。在内部作用域中如果你使用该变量名访问到的将是内部变量而外部变量变得不可访问。let message Hello; function printMessage(message) { // 报错参数 message 遮蔽了外部的 message console.log(message); // 这里打印的是参数而不是外部的 Hello }为什么 ESLint 要禁止它代码可读性差阅读代码时很难分清当前使用的是内部变量还是外部变量容易造成混淆。潜在的逻辑错误开发者可能本意是想修改外部变量结果不小心声明了一个新的局部变量导致外部变量未被更新引发难以察觉的 Bug。维护困难当代码嵌套层级较深时追踪变量的来源变得非常困难。no-empty-pattern禁止在解构赋值Destructuring Assignment中使用空的对象模式或空的数组模式。这条规则防止你写出像// 1. 空对象解构 const {} foo; // 报错Unexpected empty object pattern // 2. 空数组解构 const [] bar; // 报错Unexpected empty array pattern // 3. 函数参数中的空解构 function process({}) { // 报错 // ... } // 4. 嵌套中的空解构 const { a: {} } data; // 报错a 对应的值是空对象模式这样的代码。这种写法在 JavaScript 中虽然语法合法但通常没有任何实际意义它不会提取任何变量也不会报错只是单纯地“路过”了一下数据往往是复制粘贴错误或逻辑遗漏导致的。const { a: {} } data;在上述代码中如果 a 不存在你想让它默认为一个空对象 {}// ✅ 正确写法如果 data.a 是 undefined则 obj 默认为 {} const { a: obj {} } data; console.log(obj); // 如果 data 中没有 a这里输出 {}或者如果a不存在你只想忽略它不需要任何变量// ✅ 正确写法只提取 a不做嵌套匹配 const { a } data; // 或者如果你连变量 a 都不想要可以直接忽略 // (这种写法通常没必要除非是为了触发某些副作用但一般直接不写就行)或者如果a存在你想从a里面提取具体的属性比如name// ✅ 正确写法嵌套解构 默认值 // 含义从 data 中找 a如果 a 不存在视为 {}然后从结果中找 name如果 name 也不存在默认为 Unknown const { a: { name Unknown } {} } data; console.log(name); // 情况 A: data { a: { name: Alice } } - 输出 Alice // 情况 B: data { a: {} } - 输出 Unknown // 情况 C: data {} - 输出 Unknown (因为 a 缺失用了默认值 {})意图❌ 错误写法 (会报错/崩溃)✅ 正确写法给默认空对象const { a: {} } data;const { a: obj {} } data;提取内部属性const { a: { name } } data;(若 a 缺失则崩溃)const { a: { name } {} } data;仅仅检查/忽略const { a: {} } data;const { a } data;
ESLint代码规范(一)
react/react-in-jsx-scope当使用 JSX 语法时React 必须在作用域内即需要显式导入 React。在 React 17 之前每次使用 JSX 语法时Babel 会将其转换为 React.createElement() 调用因此必须导入 React 才能使用 JSX。举例index.jsxexport default () divNew Page index/div;应修改为import React from react; export default () divNew Page index/div;max-leneslint默认最大行长度为100处理方法修改eslint配置项.eslintrc.jsconst { strictEslint } require(umijs/fabric); module.exports { ...strictEslint, globals: { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, page: true, }, rules: { ...strictEslint.rules, max-len: [error, { code: 100, // 最大行长度 ignoreComments: true, // 忽略注释行 ignoreStrings: true, // 忽略字符串字面量避免因长 URL 或模板字符串报错 ignoreTemplateLiterals: true, // 忽略模板字符串 ignoreRegExpLiterals: true, // 忽略正则表达式 ignoreUrls: true, // 忽略 URL }], }, }注意如果注释行为JSX注释将不被eslint视为注释行在 ESLint 的 max-len 规则中 ignoreComments: true 只适用于 真正的注释 // 单行注释/* 多行注释 */而 JSX 中的 {/* ... */} 虽然在语义上是注释但在 AST抽象语法树中它被解析为{ - JSX 表达式开始/* ... */ - 内部的注释} - JSX 表达式结束因此ESLint 的 max-len 规则 不将 JSX 注释视为纯注释行 仍然会检查其长度。举例const SubmitFormItem ({ className, ...rest }) { const clsString classNames(styles.submit, className); return ( FormItem {/* Button sizelarge className{clsString} typeprimary htmlTypesubmit {...rest} / */} Button sizelarge className{clsString} htmlTypesubmit {...rest} / /FormItem ); };template literalsESLint 推荐使用模板字符串template literals而不是字符串拼接。const title{添加${title}} // 模态框标题Parsing error: Expression expected.const page service?.data?.page || {};在老旧项目中可选链操作符(?. )是ES2020的特性可能ESLint的解析器配置没有正确支持这个语法。修改配置项添加ES2020的特性支持.eslintrc.jsconst { strictEslint } require(umijs/fabric); module.exports { ...strictEslint, rules: { ... }, overrides: [ { files: [*.js, *.jsx], parser: babel-eslint, parserOptions: { ecmaVersion: 2020, ecmaFeatures: { jsx: true, }, }, }, ], };这样当执行npm run lint命令时就不会再因为可选链操作符而报错了变量 is defined but never used方法1使用ESLint自动修复# 自动修复所有可修复的问题包括未使用的变量 npx eslint --fix src/ # 或者使用 yarn yarn eslint --fix src/ # 或者 npm npm run lint:fix注意以下未使用对象需手动删除ESLint 无法确定是否真的不需要未使用的导入未使用的函数变量解决方案手动删除、使用代码清理工具方法2使用代码清理工具eslint-plugin-unused-imports 专门用于检测和移除未使用的导入安装插件# 使用 npm npm install --save-dev eslint-plugin-unused-imports typescript-eslint/eslint-plugin typescript-eslint/parser # 或使用 yarn yarn add --dev eslint-plugin-unused-imports typescript-eslint/eslint-plugin typescript-eslint/parser配置.eslintrc.jsmodule.exports { parser: typescript-eslint/parser, plugins: [ typescript-eslint, unused-imports // 添加插件 ], rules: { // 配置未使用导入的规则 unused-imports/no-unused-imports: error, unused-imports/no-unused-vars: [ error, { vars: all, varsIgnorePattern: ^_, args: after-used, argsIgnorePattern: ^_ } ], // 其他规则... } };运行以下命令来自动修复未使用的导入# 自动修复所有可修复的问题 npx eslint --fix src/ # 或使用 yarn yarn eslint --fix src/集成到开发流程可以将 ESLint 检查添加到项目的 package.json 脚本中scripts: { lint: eslint src/, lint:fix: eslint --fix src/ }方法3修改ESLint配置不推荐可能导致代码质量下降配置规则为警告而非错误rules: { ...strictEslint.rules, eqeqeq: off, no-unused-vars: off, typescript-eslint/no-unused-vars: warn, }或者允许忽略特定模式的未使用变量module.exports { ...strictEslint, rules: { ...strictEslint.rules, typescript-eslint/no-unused-vars: [warn, { vars: all, varsIgnorePattern: ^_|^React$, // 忽略 React 和以 _ 开头的变量 args: after-used, argsIgnorePattern: ^_ }], }, };^_ - 忽略以 _ 开头的变量如 _unused ^React$ - 忽略 React因为 JSX 需要Unary operator used意思是禁止使用自增运算符 例如 i 或 i1. 为什么会有这个规则这条规则通常出现在追求函数式编程风格或代码绝对清晰的项目中如 Airbnb 风格指南。主要理由包括隐式副作用i不仅返回值还修改变量i。在某些复杂的表达式中如array[i] i这可能导致代码难以阅读且容易出错。自动分号插入问题在某些极端情况下紧跟在行尾可能会引起解析歧义虽然现代工具处理得很好但这是一个历史遗留顾虑。鼓励显式操作强制开发者写出更明确的意图比如i 1。2. 如何解决配置.eslintrc.jsno-plusplus: off,或者// 显式加 1 i 1; // 在循环中 for (let i 0; i len; i 1) { // ... }no-nested-ternary不允许嵌套多个三元运算符代码阅读不友好可以改为if elseno-underscore-dangle在代码中使用了以下划线 _ 开头或结尾的标识符variable/property name这违反了 ESLint 的 no-underscore-dangle 规则。no-constant-condition禁止在控制流语句如if、while、for、条件运算符?:中使用常量表达式作为条件。// 1. 典型的错误写法 if (a 5) { // 报错这是一个赋值表达式结果永远是 5 (truthy)意图可能是 a 5 // ... } // 2. 逻辑运算结果恒定 if (foo || true) { // 报错无论 foo 是什么结果永远为真 // ... } // 3. 三元运算符中的常量 var a true ? foo : bar; // 报错条件恒定为真bar 永远无法执行为什么要有这条规则防止死循环while (true)如果没有内部的break会导致程序卡死。虽然有时故意写while(true)但 ESLint 希望你显式地关闭该规则或使用注释说明。发现拼写错误最常见的是把比较运算符或误写成了赋值运算符。例如if (x 5)实际上是将 5 赋值给 x然后判断 5真这通常不是开发者的本意。移除死代码如果条件是if (false)那么里面的代码永远不会执行这是无用代码。array-callback-returnmap() 方法期望回调函数 返回一个值 来构成新数组但这里的回调函数没有 return 语句将map修改为forEachno-shadow变量遮蔽禁止变量声明遮蔽Shadowing外部作用域中已存在的变量。变量遮蔽当一个内部作用域如函数内部、代码块内部声明了一个与外部作用域同名的变量时内部变量就会“遮蔽”外部变量。在内部作用域中如果你使用该变量名访问到的将是内部变量而外部变量变得不可访问。let message Hello; function printMessage(message) { // 报错参数 message 遮蔽了外部的 message console.log(message); // 这里打印的是参数而不是外部的 Hello }为什么 ESLint 要禁止它代码可读性差阅读代码时很难分清当前使用的是内部变量还是外部变量容易造成混淆。潜在的逻辑错误开发者可能本意是想修改外部变量结果不小心声明了一个新的局部变量导致外部变量未被更新引发难以察觉的 Bug。维护困难当代码嵌套层级较深时追踪变量的来源变得非常困难。no-empty-pattern禁止在解构赋值Destructuring Assignment中使用空的对象模式或空的数组模式。这条规则防止你写出像// 1. 空对象解构 const {} foo; // 报错Unexpected empty object pattern // 2. 空数组解构 const [] bar; // 报错Unexpected empty array pattern // 3. 函数参数中的空解构 function process({}) { // 报错 // ... } // 4. 嵌套中的空解构 const { a: {} } data; // 报错a 对应的值是空对象模式这样的代码。这种写法在 JavaScript 中虽然语法合法但通常没有任何实际意义它不会提取任何变量也不会报错只是单纯地“路过”了一下数据往往是复制粘贴错误或逻辑遗漏导致的。const { a: {} } data;在上述代码中如果 a 不存在你想让它默认为一个空对象 {}// ✅ 正确写法如果 data.a 是 undefined则 obj 默认为 {} const { a: obj {} } data; console.log(obj); // 如果 data 中没有 a这里输出 {}或者如果a不存在你只想忽略它不需要任何变量// ✅ 正确写法只提取 a不做嵌套匹配 const { a } data; // 或者如果你连变量 a 都不想要可以直接忽略 // (这种写法通常没必要除非是为了触发某些副作用但一般直接不写就行)或者如果a存在你想从a里面提取具体的属性比如name// ✅ 正确写法嵌套解构 默认值 // 含义从 data 中找 a如果 a 不存在视为 {}然后从结果中找 name如果 name 也不存在默认为 Unknown const { a: { name Unknown } {} } data; console.log(name); // 情况 A: data { a: { name: Alice } } - 输出 Alice // 情况 B: data { a: {} } - 输出 Unknown // 情况 C: data {} - 输出 Unknown (因为 a 缺失用了默认值 {})意图❌ 错误写法 (会报错/崩溃)✅ 正确写法给默认空对象const { a: {} } data;const { a: obj {} } data;提取内部属性const { a: { name } } data;(若 a 缺失则崩溃)const { a: { name } {} } data;仅仅检查/忽略const { a: {} } data;const { a } data;