本地构建正常,GitHub Actions构建时Webpack模块解析失败
容器微前端GitHub Actions构建失败:模块找不到错误
将代码推送到GitHub后,GitHub Actions执行npm install和npm run build时出现以下错误:
ERROR in ./src/App.js ... Module not found: Error: Can't resolve './components/navbar/Progress' in '/home/runner/work///packages/container/src'
GitHub Actions错误信息
控制台日志核心提示:无法解析./components/navbar/Progress模块路径
webpack.common.js配置
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { module: { rules: [ { test: /\.m?js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'], plugins: ['@babel/plugin-transform-runtime'], }, }, }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.(scss)$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }, ] }, { test: /\.svg$/, use: ['@svgr/webpack'], }, { test: /\.(png|jpg|jpeg|gif|ico)$/, use: [ { loader: 'file-loader', options: { name: '[path][name].[ext]?hash=[hash:20]', limit: 8192 }, }, ], }, { test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/, use: [{ loader: 'file-loader' }] }, { test: /\.(ttf|eot|woff|woff2)$/, loader: 'file-loader', options: { name: 'fonts/[name].[ext]' }, }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html' }), ], resolve: { extensions: ['', '.js', '.jsx', '.scss', '.eot', '.ttf', '.svg', '.woff'], modules: ['node_modules', 'scripts', 'images', 'fonts'], }, };
webpack.prod.js配置
const { merge } = require('webpack-merge'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const commonConfig = require('./webpack.common'); const packageJson = require('../package.json'); const domain = process.env.PRODUCTION_DOMAIN; const prodConfig = { mode: 'production', output: { filename: '[name].[contenthash].js', publicPath: '/container/latest/', }, plugins: [ new ModuleFederationPlugin({ name: 'container', filename: 'remoteEntry.js', remotes: { marketingMfe: `marketingMod@${domain}/marketing/latest/remoteEntry.js`, authMfe: `authMod@${domain}/auth/latest/remoteEntry.js`, companyMfe: `companyMod@${domain}/company/latest/remoteEntry.js`, }, exposes: { './Functions': './src/functions/Functions', './Variables': './src/data-variables/Variables' }, shared: {...packageJson.dependencies, ...packageJson.peerDependencies}, }), ], }; module.exports = merge(commonConfig, prodConfig);
容器微前端目录结构
| config | public | | index.html | src | | assets | | components | | | navbar | | | ...some components | | data-variables | | | Variables.js | | functions | | | Functions.js | | App.js | | bootstrap.js | | index.js
App.js核心代码
import React, { lazy, Suspense, useState, useEffect } from 'react'; import { Router, Route, Switch, Redirect } from 'react-router-dom'; import { createBrowserHistory } from 'history'; import { createGlobalStyle } from 'styled-components'; import WebFont from 'webfontloader'; WebFont.load({ google: { families: ['Barlow', 'Playfair Display', 'Overpass', 'Montserrat'] } }); import Progress from './components/navbar/Progress'; import Header from './components/Header'; const MarketingLazy = lazy(() => import('./components/MarketingApp')); const AuthLazy = lazy(() => import('./components/AuthApp')); const CompaniesLazy = lazy(() => import('./components/CompanyApp')); const history = createBrowserHistory(); const GlobalStyle = createGlobalStyle` body { height: 100%; min-height: 100vh; margin: 0; padding: 0; } html { height: 100%; } ` export default function App() { const [isSignedIn, setIsSignedIn] = useState(false); useEffect(() => { if (isSignedIn) history.push('/companies/last') }, [isSignedIn]); return ( <Router history={history}> <div> <GlobalStyle /> <Header onSignOut={() => setIsSignedIn(false)} isSignedIn={isSignedIn} /> <Suspense fallback={<Progress />} > <Switch> <Route path="/auth/*"> <AuthLazy onSignIn={() => setIsSignedIn(true)} /> </Route> <Route path="/companies/last"> { !isSignedIn && <Redirect to="/" />} <CompaniesLazy /> </Route> <Route path="/companies"> { !isSignedIn && <Redirect to="/" />} <CompaniesLazy /> </Route> <Route path="/user"> { !isSignedIn && <Redirect to="/" />} <CompaniesLazy /> </Route> <Route path="/" component={MarketingLazy} /> </Switch> </Suspense> </div> </Router> ); };
解决步骤
- 检查文件路径与大小写:GitHub Actions运行在Linux环境,文件名严格区分大小写。确认本地
src/components/navbar/下的Progress组件文件名(如Progress.js/Progress.jsx)与导入语句的大小写完全一致,且文件确实存在。 - 修复webpack扩展名配置:当前
resolve.extensions中的空字符串''可能导致解析异常,建议移除,修改为:resolve: { extensions: ['.js', '.jsx', '.scss', '.eot', '.ttf', '.svg', '.woff'], modules: ['node_modules', 'scripts', 'images', 'fonts'], }, - 确认文件已提交到仓库:检查GitHub仓库中是否存在
src/components/navbar/Progress相关文件,避免本地存在但未提交的情况。 - 同步目录结构:确保本地目录结构与仓库完全一致,无嵌套错误或文件位置变更未同步的问题。
内容的提问来源于stack exchange,提问作者melv3223
相关产品推荐
相关产品推荐

