带你彻底搞懂 ESLint 扁平配置(Flat Config),告别
'React' must be in scope和各种奇怪的插件兼容问题。
前言
最近在使用 Vite + React 19 + TypeScript 搭建项目时,想接入 ESLint 来规范代码。本以为运行 pnpm create @eslint/config 就能一键搞定,结果却连续踩了五六个坑:
'React' must be in scope when using JSXTypeError: Plugin config "jsx-runtime" ... cannot be used in this contextTypeError: contextOrFilename.getFilename is not a functionWarning: React version not specifiedPlugin "plugin:react" not found
经过一番研究,我终于梳理出了一套稳定、干净、无报错的 ESLint 9 扁平配置方案。本文将完整记录踩坑过程与最终解决方案,希望对遇到类似问题的同学有所帮助。
一、为什么选择 ESLint 9 扁平配置?
ESLint 从 9.0 开始将扁平配置(Flat Config)作为默认配置格式,取代了传统的 .eslintrc 方式。扁平配置的核心是一个导出的数组,每个对象可以包含 files、ignores、languageOptions、plugins、rules 等字段,配置组合更加直观和强大。
然而,官方脚手架 pnpm create @eslint/config 生成的配置往往存在兼容性问题,尤其是与 eslint-plugin-react 和 @typescript-eslint 配合使用时。
二、最终可用的完整配置文件
先上成品,不想看过程可以直接复制以下 eslint.config.js:
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import reactPlugin from "eslint-plugin-react";
import { defineConfig } from "eslint/config";export default defineConfig([// 1. 忽略构建目录{ignores: ["dist", "node_modules", "coverage"],},// 2. 自动检测 React 版本(消除警告){settings: {react: {version: "detect",},},},// 3. ESLint 官方 JS 推荐规则js.configs.recommended,// 4. TypeScript ESLint 推荐规则...tseslint.configs.recommended,// 5. 手动配置 React 规则(关键!不使用预设){files: ["**/*.{js,jsx,ts,tsx}"],plugins: {react: reactPlugin,},languageOptions: {globals: globals.browser,parserOptions: {ecmaFeatures: {jsx: true,},},},rules: {// React 17+ 不再需要导入 React"react/react-in-jsx-scope": "off","react/jsx-uses-react": "off",// 安全规则:强制 target="_blank" 必须带 rel"react/jsx-no-target-blank": "error",// 确保 JSX 中使用的变量不会被误报未使用"react/jsx-uses-vars": "error",// 关闭有兼容性问题的规则(若希望启用可尝试升级插件)"react/display-name": "off",// 可选:关闭不常用的规则"react/prop-types": "off",},},
]);
运行 pnpm run lint 即可看到代码规范检查正常执行,无报错,无 React 导入错误。
三、一路踩过的坑 & 为什么这样写
下面按我实际遇到的错误顺序,解释各个坑的成因及解决思路。
坑1:'React' must be in scope
表现:使用 JSX 时要求显式导入 React,即使项目使用 React 17+ 新 JSX 转换。
错误原因:eslint-plugin-react 默认开启了 react/react-in-jsx-scope 规则,要求每个 JSX 文件顶部都要 import React from 'react'。虽然可以关闭该规则,但在扁平配置中规则覆盖顺序很容易出错。
解决方案:在配置对象的 rules 中显式关闭,并确保该对象位于数组最后(或至少在其他可能开启该规则的配置之后)。上面最终配置中,所有 React 规则都在一个独立对象内,且放在 js.configs.recommended 和 tseslint.configs.recommended 之后。
坑2:Plugin config "jsx-runtime" ... cannot be used in this context
表现:使用 extends: ["js/recommended", "react/jsx-runtime"] 时报错。
错误原因:字符串 "react/jsx-runtime" 是传统 .eslintrc 格式的预设引用,无法直接用于扁平配置。虽然 defineConfig 支持部分 extends 语法,但 "plugin:react/jsx-runtime" 这种带 plugin: 前缀的写法在扁平配置中需要额外配置 plugins 映射,且容易出错。
解决方案:完全摒弃 extends 字符串,改用手动规则配置。直接写 "react/react-in-jsx-scope": "off" 更直接可靠。
坑3:TypeError: contextOrFilename.getFilename is not a function
表现:使用了 pluginReact.configs.flat.recommended 预设后,ESLint 在检查文件时崩溃。
错误原因:eslint-plugin-react 的某些规则(如 react/display-name)内部调用了 context.getFilename(),但在 ESLint 10 的扁平配置中,context 对象结构发生了变化,导致该函数未定义。这是插件与 ESLint 10 的兼容性问题,目前(插件 v7.37.5)仍未完全修复。
解决方案:放弃使用 pluginReact.configs.flat.recommended 预设,手动注册插件并只启用实际需要的规则。这样绕开了有问题的 react/display-name 规则(将其设为 off)。如果你确实需要该规则,可尝试降级 ESLint 到 8.x,但长远来看建议等待插件更新。
坑4:Warning: React version not specified
表现:控制台输出警告,提示未指定 React 版本。
错误原因:eslint-plugin-react 需要知道项目使用的 React 版本,以便正确应用某些规则。
解决方案:在配置中添加 settings.react.version: "detect",插件会自动读取 package.json 中的 React 版本。
坑5:Plugin "plugin:react" not found
表现:写了 extends: ["plugin:react/recommended"] 后报错。
错误原因:扁平配置中使用 plugin: 前缀的字符串时,必须同时提供 plugins 映射(如 plugins: { react: pluginReact }),且字符串前缀 plugin: 不能去除。但即使映射正确,仍可能因其他原因失败。
解决方案:既然我们放弃了 extends 字符串,这个坑自然就不存在了。
四、核心配置要点总结
经过反复试验,下面几点是 ESLint 9 + React + TypeScript 稳定配置的必要条件:
- 不要使用
pluginReact.configs.flat.recommended预设(至少目前版本有兼容问题)。 - 不要使用
extends字符串(如"plugin:react/recommended"或"react/jsx-runtime")。 - 手动注册 React 插件:
plugins: { react: reactPlugin }。 - 手动关闭
react/react-in-jsx-scope。 - 通过
settings.react.version指定版本(推荐"detect")。 - 自定义规则对象放在数组末尾,以确保覆盖任何先前可能开启的规则。
- 使用
defineConfig辅助函数(从eslint/config导入)可以获得更好的类型提示和extends支持,但即使不使用它,直接导出数组也是有效的。
五、拓展:集成 Prettier 和 Git 钩子
如果你还需要代码格式化工具和提交前自动检查,可以继续添加:
pnpm add -D prettier eslint-config-prettier husky lint-staged
然后在 eslint.config.js 最后加入:
import prettierConfig from "eslint-config-prettier";
export default defineConfig([// ... 之前的配置prettierConfig, // 关闭所有与 Prettier 冲突的规则
]);
配置 lint-staged:
// package.json
{"lint-staged": {"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"]}
}
初始化 Husky:
npx husky init
echo "npx lint-staged" > .husky/pre-commit
六、结语
ESLint 9 的扁平配置带来了更强大的灵活性,但也让插件兼容性和配置写法变得更复杂。如果你正从旧版迁移或新建项目,希望本文能帮你少走弯路。
最终,一个稳定可用的 ESLint 配置不一定需要复杂的 extends 链或官方预设。手动注册插件 + 按需开启规则 反而更清晰、更可控。
如果你在配置中遇到了其他奇怪的问题,欢迎留言交流。也建议关注 eslint-plugin-react 的后续版本,或许未来 flat.recommended 预设会完全修复这些兼容问题,届时我们再更新配置方案。
本文档基于 ESLint 10.4.0、eslint-plugin-react 7.37.5、typescript-eslint 8.59.2 环境验证。
