eslint配置实践

Q: 如果我的eslintrc是这样配置的:

js 复制代码
module.exports = {
  extends: [
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:jsx-a11y/recommended',
    'plugin:prettier/recommended',
    '@repo/eslint-config/strict',
    '@repo/eslint-config/react',
    '@repo/eslint-config/typescript',
    '@repo/eslint-config/prettier',
  ],
  rules: {
    // 'react/jsx-uses-vars': 'error',
  },
};

那么多个extends的结果,是jsonmerge覆盖吗 还是相同的配置会被deep merge?

Answer:

ESLint 在处理 extends 时,行为不是一个"深度 merge",而是有明确的合并/覆盖规则:

1. 规则(rules)的合并方式

  • 各个 extends 里的 rules 最终会合并成一个对象

  • 同一个规则 如果在多个配置里都出现,后面的会覆盖前面的(即后者优先级更高)。

  • 合并是 浅层级的,不会做 deep merge。例如:

    js 复制代码
    // 第一个 extends
    { rules: { 'no-unused-vars': ['error', { vars: 'all' }] } }
    
    // 第二个 extends
    { rules: { 'no-unused-vars': ['warn'] } }

    最终结果就是 { 'no-unused-vars': ['warn'] },前一个的 { vars: 'all' } 整个丢掉。

2. 其他配置项的合并

  • parserOptionssettings 等对象字段:同样是 浅合并,同名 key 后者覆盖前者。
  • plugins:会做合并去重(数组拼接,不会覆盖)。
  • env:多个配置里的布尔值会合并(true 会"叠加")。

3. 你的例子里

js 复制代码
extends: [
  'plugin:react/recommended',
  '@yhdfe/eslint-config/strict',
  '@yhdfe/eslint-config/react',
  '@yhdfe/eslint-config/typescript',
  '@yhdfe/eslint-config/prettier',
],
  • 最终生效的配置是按顺序从前到后合并的。
  • 如果 plugin:react/recommended 打开了 react/jsx-uses-vars,但你后面的某个包(例如 @yhdfe/eslint-config/react)把它关掉了,那最终就是"被关掉"。
  • 如果你在 rules 里再手动写一遍,那就是 最终权威值(最高优先级)。

4. 怎么验证?

你可以直接运行:

arduino 复制代码
npx eslint --print-config index.js

它会输出 index.js 最终生效的完整配置(合并后的结果),这样就能直观看到 react/jsx-uses-vars 是什么状态。

总结

  • extends 的合并是 浅合并,同名规则或设置,后者覆盖前者。
  • pluginsenv 这类会做合并,rulesparserOptionssettings 这类就是浅覆盖。
  • 想知道最终结果,用 --print-config 最靠谱。

Q:我有一个index.js文件里,有这样的代码:

js 复制代码
import React from 'react';
import ReactDom from 'react-dom';
import ToastContainer from './ToastContainer';

const toastContainerDiv = document.createElement('div');
document.body.appendChild(toastContainerDiv);

const getToastContainerRef = () => {
  return ReactDom.render(<ToastContainer />, toastContainerDiv);
};

看代码的话,ToastContainer这个变量有被调用,但是报了eslint error:'ToastContainer' is defined but never used.eslint(no-unused-vars)。

以下是我的eslint配置文件:

js 复制代码
require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = {
    parser: '@babel/eslint-parser',
    parserOptions: {
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true,
        },
        requireConfigFile: false,
        babelOptions: {
            presets: ['@babel/preset-react'],
        },
    },
    plugins: ['react', 'react-hooks'],
    settings: {
        react: {
            version: 'detect',
        },
    },
    rules: {
        // eslint built-in rules
        "no-param-reassign": 2, // 禁止对函数参数进行重新赋值
        "no-prototype-builtins": 0, // 允许使用Object.prototype的内置方法
        // eslint-plugin-react规则
        "react/display-name": 0, // 允许组件缺少displayName属性
        "react/jsx-key": 1, // 警告缺少key属性的jsx元素
        "react/no-array-index-key": 1, // 警告在jsx元素中使用数组索引作为key
        "react/prop-types": 0, // 允许缺少propTypes属性
        "react/react-in-jsx-scope": 0, // 允许在jsx中使用React变量而不导入React模块
        "react/self-closing-comp": 1, // 警告没有子元素的自闭合组件
        // eslint-plugin-react-hooks规则
        "react-hooks/exhaustive-deps": 1, // 警告useEffect和useCallback的依赖项不完整
        "react-hooks/rules-of-hooks": 2, // 强制遵守React Hooks的规则
        // eslint-plugin-jsx-a11y规则
        "jsx-a11y/accessible-emoji": "off", // 关闭使用emoji的警告
        "jsx-a11y/click-events-have-key-events": 0, // 允许点击事件没有对应的键盘事件
        "jsx-a11y/no-static-element-interactions": 0, // 允许静态HTML元素没有点击事件时不需要增加role属性
        "jsx-a11y/anchor-is-valid": [
            "warn",
            {
                components: ["Link"],
                specialLink: ["hrefLeft", "hrefRight"],
                aspects: ["invalidHref", "preferButton"],
            },
        ], // 警告无效的锚点链接和推荐使用按钮代替的情况
        "jsx-a11y/anchor-has-content": [
            0,
            {
                components: ["Anchor"],
            },
        ], // 允许锚点元素没有内容
    },
};

分析一下为什么会有这个eslint的误报,要如何修复?

Answer:

好------我一步步把可能的原因和可行的修复都列清楚,结论先说在最前面:

结论(最可能) :ESLint 的核心规则 no-unused-varsToastContainer is defined but never used,是因为 ESLint 默认不会把 JSX 中使用的组件名 当作"已使用的变量"来识别。eslint-plugin-react 提供了把 JSX 用法标记成"已使用变量"的规则(react/jsx-uses-vars / 以及历史上的 react/jsx-uses-react),你当前的配置没有启用这类规则(或插件/版本未生效),所以才会误报。

下面按步骤分析与修复(包含可选方案、调试检查项和最小改动示例)。

一、为什么会发生(详细分析)

  1. ESLint core 的 no-unused-vars 规则 只看"变量在 AST 中是否被引用",但旧版/纯核心并不会把 JSX 元素 <ToastContainer /> 的出现自动视为对 ToastContainer 变量的引用。

  2. eslint-plugin-react 提供了两个帮助类规则来解决这个问题:

    • react/jsx-uses-react ------ 告诉 ESLint React(旧的 JSX runtime)在 JSX 中被使用(用于避免 React 被误判为未使用)。
    • react/jsx-uses-vars ------ 告诉 ESLint "以大写开头的 JSX 标识符在 JSX 中是被使用的变量",避免组件名被 no-unused-vars 误判。
  3. 你的配置里虽然有 plugins: ['react', 'react-hooks']settings.react.version 也有 detect,但你并没有 显式启用 react/jsx-uses-vars,也没有 extends: ['plugin:react/recommended'] 之类去自动打开这些关联规则。因此 ESLint 仍然把 ToastContainer 视作"定义但未使用"。

另外可能的次要原因(排查项):

  • eslint-plugin-react 没有真实安装(plugins 写了但依赖缺失) → 插件规则不会生效。
  • 使用了不兼容的插件/ESLint 版本(插件老或者 ESLint 太老),导致规则名不存在或被忽略。
  • ESLint 缓存导致旧报错仍显示(--no-cache 可排查)。

二、推荐的最小修复(优先推荐)

在你现在的 .eslintrcrules加一条

js 复制代码
// 在现有 module.exports.rules 内添加
"react/jsx-uses-vars": "error",

完整示例片段(只展示关键位置):

js 复制代码
module.exports = {
  parser: '@babel/eslint-parser',
  parserOptions: { /* ...你原来的配置... */ },
  plugins: ['react', 'react-hooks'],
  settings: { react: { version: 'detect' } },
  rules: {
    "react/jsx-uses-vars": "error",

    // 你原来的规则...
    "no-param-reassign": 2,
    "no-prototype-builtins": 0,
    // ...
  },
};

这样做后 eslint 会把 <ToastContainer /> 的使用识别为对 ToastContainer 的引用,从而消除 no-unused-vars 的误报。

三、其他可选方案(依据场景选择)

A. 使用插件的推荐配置(会带来更多 React 规则)

js 复制代码
extends: [
  'plugin:react/recommended'
],

优点:自动启用一批推荐规则(包含避免 JSX 误报的规则)。缺点:会启用很多规则,可能需要额外调整。

B. 如果你使用 React 17+ 的新 JSX 转换(automatic runtime)
eslint-plugin-react 提供 plugin:react/jsx-runtime 的 extend(取决于插件版本)。如果你使用新 transform,可以考虑加上它。但最简洁仍是单独打开 react/jsx-uses-vars

C. 文件级/行级忽略(不推荐,临时可用)

在 import 那行加注释:

js 复制代码
import ToastContainer from './ToastContainer'; // eslint-disable-line no-unused-vars

只用于临时屏蔽报错,不推荐作为长期解决方案。

四、调试步骤(如果上面修复不生效,请按这个顺序检查)

  1. 确认 eslint-plugin-react 真安装:npm ls eslint-plugin-reactpnpm ls eslint-plugin-react
  2. 清除 ESLint 缓存后重跑:eslint --no-cache index.js
  3. 打印 eslint 生效的配置查看最终是否包含规则:npx eslint --print-config index.js,看输出中是否包含 "react/jsx-uses-vars" : "error"
  4. 若 ESLint 或 plugin 版本较旧,考虑升级 eslinteslint-plugin-react 到较新的稳定版本(通常能避免兼容性问题)。

五、补充说明(容易混淆的点)

  • 你把 react/react-in-jsx-scope 设为 0(允许 JSX 时不导入 React)这和 ToastContainer 的误报无关。react-in-jsx-scope 只是控制 React 是否必须在文件中声明。
  • react/jsx-uses-react(标记 React 被使用)跟 react/jsx-uses-vars(标记 JSX 元素名被使用)是两个不同作用的规则;这里我们关心后者。

六、快速结论与建议

  • 最简单、最小改动 :在 ESLint 配置里加入 "react/jsx-uses-vars": "error",然后重跑 ESLint。
  • 若你愿意接受更多 React 相关检查,改用 extends: ['plugin:react/recommended']
  • 如果仍不生效,请确认 eslint-plugin-react 已安装且 ESLint 不是用缓存运行,并用 --print-config 检查最终配置。
相关推荐
诚实可靠王大锤4 小时前
react-native集成PDF预览组件react-native-pdf
前端·react native·react.js·pdf
智慧源点5 小时前
解决 Vite + React 项目部署 GitHub Pages 的完整指南:从 404 到成功部署
前端·react.js·github
yourkin6667 小时前
初识react
前端·javascript·react.js
゜ eVer ㄨ7 小时前
React第四天——hooks
前端·react.js·前端框架
江城开朗的豌豆9 小时前
React Ref揭秘:直接操作DOM的"秘密通道"
前端·react.js
江城开朗的豌豆9 小时前
何时该请出Redux?前端状态管理的正确打开方式
前端·javascript·react.js
江城开朗的豌豆9 小时前
Redux的双面人生:天使还是恶魔?
前端·javascript·react.js
困惑阿三1 天前
React 展示Markdown内容
前端·react.js·前端框架
Z_Wonderful1 天前
同时使用ReactUse 、 ahooks与性能优化
react.js·性能优化·typescript