如何让Create React App项目中外部模块的SVG React组件导入正常工作?
这个问题的核心在于Create React App(CRA)的webpack默认配置规则:
CRA内置的@svgr/webpack loader只会对项目自身src目录下的SVG文件进行处理,将其转换为React组件(也就是你用到的ReactComponent导出)。而对于node_modules目录下的文件,CRA默认会用file-loader处理SVG,这时候导出的是SVG文件的路径字符串,而非React组件。
当你的@foo/ui组件库通过tsc编译后,只是把TypeScript转成了JavaScript,import { ReactComponent as Logo } from '@foo/design/static/img/logo.svg'这条导入语句被原封不动保留了下来。当@foo/project尝试解析这条来自node_modules/@foo/ui的导入时,webpack不会触发@svgr/webpack,自然找不到ReactComponent导出,就会报错。
而你直接在@foo/project的根组件里导入SVG时,因为文件路径属于项目src目录(或者说webpack默认的处理范围),所以@svgr/webpack正常工作,SVG被转为React组件。
因为你的组件库是仅内部使用的,不需要对外发布到npm,这里推荐几个低成本的解决方案:
方案1:提前将SVG转为React组件(最直接)
不需要依赖任何打包配置修改,直接把SVG文件转换成React组件代码,嵌入到@foo/ui中:
- 把
@foo/design/static/img/logo.svg的内容复制出来,创建一个Logo.tsx组件:
// @foo/ui/src/components/Logo.tsx import React, { FC } from 'react'; export const Logo: FC = () => ( <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> {/* 这里粘贴原SVG的path等内容 */} <path d="M..." fill="#000" /> </svg> );
- 在
MyComponent中导入这个React组件,而非直接导入SVG:
// MyComponent.ts import React, { FC } from 'react'; import { Logo } from './Logo'; // 换成你的组件路径 export const MyComponent: FC = () => { return ( <div> Some stuff. <Logo /> </div> ); };
这样tsc编译后,MyComponent.js里就没有SVG导入语句了,@foo/project使用时完全不需要处理SVG,直接用组件即可。
方案2:用craco/react-app-rewired修改项目webpack配置
如果你不想手动转SVG,可以修改@foo/project的webpack配置,让它对node_modules/@foo/design下的SVG启用@svgr/webpack:
- 安装
craco(比react-app-rewired更简洁的CRA配置修改工具):
npm install @craco/craco --save-dev
- 在项目根目录创建
craco.config.js:
module.exports = { webpack: { configure: (webpackConfig) => { // 找到CRA中处理SVG的规则 const svgRule = webpackConfig.module.rules.find(rule => rule.oneOf?.some(item => item.test?.toString() === '/\\.svg$/') ); if (svgRule) { // 让规则包含@foo/design下的SVG文件 svgRule.oneOf.forEach(item => { if (item.test?.test('.svg')) { item.include = [ ...(item.include || []), /node_modules\/@foo\/design/ ]; } }); } return webpackConfig; } } };
- 修改
package.json的脚本命令,用craco替换react-scripts:
"scripts": { "start": "craco start", "build": "craco build", "test": "craco test" }
之后重启项目,webpack就会正确处理@foo/ui中导入的@foo/design下的SVG了。
方案3:给组件库添加打包工具(适合大量SVG场景)
如果你的@foo/ui有很多SVG需要处理,可以用Rollup代替纯tsc编译,配合@svgr/rollup插件把SVG转为React组件后再打包:
- 安装依赖:
npm install rollup @rollup/plugin-typescript @svgr/rollup --save-dev
- 创建
rollup.config.js:
import typescript from '@rollup/plugin-typescript'; import svgr from '@svgr/rollup'; export default { input: 'src/index.ts', // 你的组件库入口文件 output: { dir: 'lib', format: 'esm', sourcemap: true }, plugins: [ svgr({ exportType: 'named', jsxRuntime: 'automatic' }), typescript({ tsconfig: './tsconfig.json' }) ], external: ['react', 'react-dom'] // 排除react相关依赖,让使用方提供 };
- 修改
package.json的编译命令:
"scripts": { "build": "rollup -c" }
这样打包后的@foo/ui/lib里的代码已经把SVG转为React组件了,@foo/project直接导入就能用,不需要额外配置。
内容的提问来源于stack exchange,提问作者Soullivaneuh

