基于Webpack的React微前端项目图片加载404问题求助
微前端Module Federation图片加载404问题解决
问题说明
开发基于Module Federation的React Webpack微前端项目,现状:
- 主机应用运行在
http://localhost:3000/ - 微前端应用运行在
http://localhost:3001 - 图片资源存放在微前端应用中,微前端单独运行时图片加载正常,但嵌入主机应用后图片返回404错误
需求:所有React组件保留在微前端应用中,图片必须从3001端口加载,不能迁移到主机应用。
问题原因
组件中使用相对路径/images/default.png,当微前端嵌入主机应用时,浏览器会以主机应用的域名(3000端口)为基础请求图片,即访问http://localhost:3000/images/default.png,但图片实际存储在3001端口,因此出现404。
解决方案
1. 配置微前端Webpack公共路径
在微前端的webpack.config.js的output节点中添加publicPath,指定微前端资源的基础URL:
output: { path: path.resolve(__dirname, 'build'), filename: 'bundle.js', publicPath: 'http://localhost:3001/' // 新增公共路径配置 },
2. 修改组件图片路径引用
在微前端组件中,使用Webpack提供的__webpack_public_path__变量拼接图片路径,确保请求指向微前端的3001端口:
<img src={`${__webpack_public_path__}images/default.png`} className='shadow-md shadow-gray-400' />
3. 优化微前端Webpack图片加载规则
移除重复的图片loader配置(file-loader和url-loader重复定义了相同文件类型),合并为一套规则:
{ test: /\.(png|jpe?g|gif)$/i, use: [ { loader: 'url-loader', options: { limit: false, }, }, ], },
修改后的完整代码
微前端应用webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); const { ModuleFederationPlugin } = require('webpack').container; const packageJson = require('./package.json'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); module.exports = { mode: 'development', entry: './src/index.tsx', output: { path: path.resolve(__dirname, 'build'), filename: 'bundle.js', publicPath: 'http://localhost:3001/' // 新增公共路径配置 }, devServer: { port: 3001, hot: true, historyApiFallback: true, }, module: { rules: [ { test: /\.(?:js|ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env', '@babel/preset-typescript'], plugins: [ "@babel/plugin-transform-runtime", require.resolve('react-refresh/babel') ] } } }, { test: /\.(?:css|scss)$/, include: path.resolve(__dirname, 'src'), use: [ { loader: "style-loader" }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "postcss-loader", options: { sourceMap: true } }, { loader: "sass-loader", options: { sourceMap: true } }, ], }, // 合并后的图片加载规则 { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: 'url-loader', options: { limit: false, }, }, ], }, ], }, resolve: { extensions: ['.tsx', '.ts', '.js', '.scss', '.css'], }, plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, 'public', 'index.html'), }), new ReactRefreshWebpackPlugin(), new ModuleFederationPlugin({ name: 'McmpPortal', filename: 'remoteEntry.js', exposes: { './SupportPortal': './src/App.tsx', }, shared: { ...packageJson.dependencies } }), ], }
修改后的微前端组件
const Info = () => { return ( <div className='home-info'> <div className={`info left ${TW_CLASS.BOX_SHADOW} `}> <div className='user-info'> <div className='user-img'> {/* 使用__webpack_public_path__拼接图片路径 */} <img src={`${__webpack_public_path__}images/default.png`} className='shadow-md shadow-gray-400' /> </div> <div> <h4>John Doe</h4> <p>Senior Technical Executive</p> </div> </div> <div className='services'> <h4 className='mb-3.5'>Active Services</h4> <div className='grid grid-cols-2 gap-4'> <div className='active-services'> <div> <p><strong>Consulting Services</strong></p> <p>Expiry Date: 12 Dec 2024</p> </div> <div> <p><strong>Azure Managed Services - Gold</strong></p> <p>Expiry Date: 12 Dec 2024</p> </div> <div> <p><strong>Azure Managed Services - Platinum</strong></p> <p>Expiry Date: 12 Dec 2024</p> </div> </div> <div className='service-information-cards'> <h6>SLA</h6> <ul> <li>Scope of Services to be covered</li> <li>Performance metrics</li> <li>Penalties for contract breach</li> <li>Customer responsibilities</li> <li>Service provider responsibilities</li> </ul> </div> </div> </div> </div> <div className={`info right ${TW_CLASS.BOX_SHADOW} `}> <h4>Support Portal</h4> <p>Text</p> </div> </div> ) }
注:生产环境若微前端域名动态变化,可在入口文件中动态设置
__webpack_public_path__适配:__webpack_public_path__ = process.env.MICRO_APP_PUBLIC_PATH || 'http://localhost:3001/';
内容的提问来源于stack exchange,提问作者Noventiq
相关产品推荐
相关产品推荐

