问题
使用 Delete unused imports 操作时,同时删掉了 import React from 'react';
。

为什么不能删掉呢?因为代码 push 到远程仓库后会走 CI 流程, CI 流程里有代码检查节点进行 ESLint 检查

CI 节点的 ESLint 规则开发者无法修改或者覆盖。其中有一条规则是
json
{
"react/react-in-jsx-scope": "error"
}
它表示当 ESLint 检测到 JSX 代码但 React 没有被正确引入时,会报错。在 React 17+ 版本中,引入了新的 JSX 转换方式,可以不需要显式引入 React。为什么这条规则仍然存在,因为有大量使用低版本 React 的仓库需要兼容。
接下来我们要做的是:
- 保持 tsx 文件中存在
import React from 'react';
- 保持 delete unused imports 时,它不会被删掉
寻找原因
把鼠标放到 Button 上,显示了 2 条被检测出来的错误,一条 ESLint 检测出的,另一条是 TypeScript 的。

把鼠标放到 React 上,只显示了 1 条被检测出来的错误,是 TypeScript 的。

这表明使用 Delete unused imports 操作时,是 TypeScript 进行了删除操作, TypeScript 认为 React 是不必导入的, React 被 TypeScript 识别为未使用的变量。现在我们了解到我们要从 typescript 层面解决这个问题,可能跟 tsconfig 有关。
为什么这里没有抛出 ESLint 错误呢?因为此仓库的 ESLint 配置与 CI 代码检查节点的 ESLint 规则是相同的, 这样在出现 ESLint 错误时,本地开发使用的编辑器才能给出错误或警告。由于规则集中包含了
json
{
"react/react-in-jsx-scope": "error"
}
所以此处就不会有 ESLint 错误抛出了。
为什么 React 17- 必须 React Must Be in Scope
由于 JSX 编译为对 React.createElement
的调用,因此 React 库也必须始终在 JSX 代码的范围内。例如:
jsx
const element = <div>Hello</div>;
// React 17 及之前编译为
const element = React.createElement("div", null, "Hello");
关键点是编译后的代码直接调用了 React.createElement(),因此 React必须是一个已定义的变量。
React 17 引入了新的 JSX 转换(New JSX Transform),不再需要手动导入 React:
jsx
function Component() {
return <div>Hello</div>; // React 17+ 配合新 JSX 转换, 编译为 _jsxRuntime.jsx(...),无需 React
}
新转换会从 react/jsx-runtime
自动导入 jsx 或 jsxs 函数,不再依赖 React.createElement
。简单说就是新 JSX 转换可省略导入,但需配置工具链支持,相关工具在编译阶段帮我们做了。
解决
上面我们了解到使用 VS Code 的 Delete unused imports 操作时,是 TypeScript 进行了删除操作,我们要让 TypeScript 识别到 React 导入不是一个未被使用的变量。我们还了解了为什么 React 17 之前必须 React Must Be in Scope。
接下来我们看看仓库的 tsconfig
json
{
"extends": "./src/.umi/tsconfig.json"
}
使用了 "extends"属性来继承另一个配置文件,这是 umi 框架内置的 tsconfig,目前很多元框架都是这么做的。我们可以点跳转过去,看看这个配置文件内容,只看相关的部分
json
{
"compilerOptions": {
"jsx": "react-jsx",
}
}
jsx 属性控制JSX结构在JavaScript文件中的输出方式。它还有以下值:
- react-jsx:生成.js文件时将JSX转换为针对生产环境优化的_jsx调用
- react-jsxdev:生成.js文件时将JSX转换为仅用于开发的_jsx调用
- preserve:生成.jsx文件且保持JSX原样不变
- react-native:生成.js文件且保持JSX原样不变
- react:生成.js文件时将JSX转换为等效的 React.createElement 调用
框架内置的 jsx 被配置为 "react-jsx",这是正常的,因为当前版本的框架使用的 React 版本为 18,显然,它不需要在 tsx 或 jsx 文件顶部导入 React 了。
所以,我们就找到答案了,只要将 jsx 属性的值改为 react
就可以解决这个问题。但是我们不能修改框架内置的配置文件,怎么办呢?可以重新声明此选项以覆盖框架内置 tsconfig。
json
{
"extends": "./src/.umi/tsconfig.json",
"compilerOptions": {
"jsx": "react"
}
}
验证
修改配置后,我们来重启下 VS Code TS Server。

quick fix 菜单已经没有 Delete unused imports 选项了,只有一个未使用变量 Button 被检测到。

我们把鼠标放到 React 上查看是否还有未使用的 ts 错误抛出:

错误消失了。我们再导入一个变量验证 Delete unused imports 是否会删除 React 的导入。

只删除了未使用的变量, React 导入没有被删除,这样问题就解决了。