Create React App自定义Webpack配置未复制组件内引用图片问题
问题原因
- React组件内引用方式不被webpack识别
webpack是基于静态分析收集构建依赖的,你使用${url}/img/car.svg动态拼接路径的写法,webpack无法在编译阶段识别出这是需要处理的资源文件,自然不会将对应的图片纳入构建流程。只有scss文件里的静态路径引用、JS/JSX里通过import引入的静态资源路径,才会被webpack识别为依赖。 - 配置调用错误
你在webpack钩子中调用addMedia、bundleSass、overrideOutput三个函数时,没有传入config参数,导致你写的自定义规则根本没有被注入到webpack配置中。目前scss文件里的图片能被处理,实际上是Create React App默认的内置规则生效的结果。 - 规则优先级错误
就算你传了config参数,直接用config.module.rules.push(media)的方式加规则也不会生效。Create React App的webpack配置中,module.rules下有一个oneOf数组,所有资源处理规则都会在这个数组里匹配,匹配到第一个符合的就停止,你push到外层rules末尾的规则根本不会被执行。 - 版本兼容问题(若使用webpack5)
webpack5已经内置了Asset Modules来替代file-loader、url-loader等旧的资源处理loader,继续使用file-loader可能会和内置规则冲突,导致重复处理或者不生效。
解决方案
方案1:修正引用方式+修复配置(推荐)
- 调整React组件内的图片引用方式,改为
import静态引入:
// 先引入资源 import carSvg from '/assets/img/car.svg' // 组件内使用 <img width="34" src={carSvg} alt="car" />
- 修复配置调用时的传参问题:
webpack(config, env) { //if stage, prod, dev bundleSass(config) addMedia(config) overrideOutput(config) return config; }
- 调整
addMedia函数内的规则注入逻辑,将自定义规则插入到oneOf数组最前面,保证优先匹配:
function addMedia(config){ const media = { test: /\.(woff(2)?|ttf|eot|svg|png)(\?v=\d+\.\d+\.\d+)?$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'static/media', esModule: false // 避免和React的默认导出逻辑冲突 } } ] }; // 找到CRA内置的oneOf规则数组 const oneOfRule = config.module.rules.find(rule => Array.isArray(rule.oneOf)); // 插入到最前面,优先匹配 oneOfRule.oneOf.unshift(media); }
方案2:直接复制整个静态资源目录(适合必须用动态路径的场景)
如果你的业务场景必须用动态拼接路径的方式引用图片,可以直接用copy-webpack-plugin将/assets/img目录下的所有资源直接复制到构建产物目录,不需要webpack识别引用:
- 安装依赖:
npm install copy-webpack-plugin --save-dev - 修改配置:
const CopyPlugin = require("copy-webpack-plugin"); function addMedia(config){ config.plugins.push(new CopyPlugin({ patterns: [ { from: "assets/img", to: "static/media" }, // 有其他静态资源目录也可以继续加 ], })) }
方案3:适配webpack5内置Asset Modules(推荐,无需额外loader)
如果你的项目使用的是webpack5,可以直接替换掉file-loader,用内置能力处理资源,兼容性更好:
function addMedia(config){ const media = { test: /\.(woff(2)?|ttf|eot|svg|png)(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', generator: { filename: 'static/media/[name][ext]' } }; const oneOfRule = config.module.rules.find(rule => Array.isArray(rule.oneOf)); oneOfRule.oneOf.unshift(media); }
内容的提问来源于stack exchange,提问作者Theo Itzaris
相关产品推荐
相关产品推荐

