admin管理员组文章数量:1323723
I am currently exploring about micro frontend with Module Federation. I just forked a sandbox, tried it with success when the both modules available. It has 2 modules, app1
as the host, and app2
as the remote ponent. But as I think that each modules in module federation should be independent, I tried to make the app2
unavailable as I didn't start it. Therefore I got error when I run the app1
, it finished loading with displaying the fallback of the React's Suspense
, but milliseconds later, it bees blank as there's error I can't retrieve thus I don't really know.
After that, I tried Webpack's Promise Based Dynamic Remotes, then my webpack-config.js
bees like this:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
const path = require('path');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
static: path.join(__dirname, 'dist'),
port: 3001,
},
output: {
publicPath: 'auto',
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react'],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'app1',
remotes: {
app2: Promise((resolve) => {
const urlParams = new URLSearchParams(window.location.search);
const version = urlParams.get('app1VersionParam');
// This part depends on how you plan on hosting and versioning your federated modules
const remoteUrlWithVersion = '[app2Url]' + '/remoteEntry.js';
const script = document.createElement('script');
script.src = remoteUrlWithVersion;
script.onload = () => {
// the injected script has loaded and is available on window
// we can now resolve this Promise
const proxy = {
get: (request) => window.app1.get(request),
init: (arg) => {
try {
return window.app1.init(arg);
} catch (e) {
console.log('remote container already initialized');
}
},
};
resolve(proxy);
};
// inject this script with the src set to the versioned remoteEntry.js
document.head.appendChild(script);
}),
// "app2@[app2Url]/remoteEntry.js",
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
new ExternalTemplateRemotesPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
I tried start the app1
again, then this error es:
$ webpack serve
[webpack-cli] Failed to load '/home/projects/github-rl5uyr/app1/webpack.config.js' config
[webpack-cli] TypeError: undefined is not a promise
at Promise (<anonymous>)
at Object.eval (/home/projects/github-rl5uyr/app1/webpack.config.js:32:15)
at Object.function (.01faa899fac41642342f4b7113feacabea334fa1.js:11:114831)
at Module._pile (.01faa899fac41642342f4b7113feacabea334fa1.js:6:167880)
at Object.Module._extensions..js (.01faa899fac41642342f4b7113feacabea334fa1.js:6:168239)
at Module.load (.01faa899fac41642342f4b7113feacabea334fa1.js:6:166317)
at Function.Module._load (.01faa899fac41642342f4b7113feacabea334fa1.js:6:163857)
at Module.require (.01faa899fac41642342f4b7113feacabea334fa1.js:6:166635)
at i (.01faa899fac41642342f4b7113feacabea334fa1.js:6:427483)
at _0x5301a6 (.01faa899fac41642342f4b7113feacabea334fa1.js:11:114450)
So, can the module federations run independently each other? If not, what's the real difference as normal library dependencies of monolith front end instead of this sophisticated micro frontend, that I assumed it should be able to work independently like microservices?
I am currently exploring about micro frontend with Module Federation. I just forked a sandbox, tried it with success when the both modules available. It has 2 modules, app1
as the host, and app2
as the remote ponent. But as I think that each modules in module federation should be independent, I tried to make the app2
unavailable as I didn't start it. Therefore I got error when I run the app1
, it finished loading with displaying the fallback of the React's Suspense
, but milliseconds later, it bees blank as there's error I can't retrieve thus I don't really know.
After that, I tried Webpack's Promise Based Dynamic Remotes, then my webpack-config.js
bees like this:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
const path = require('path');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
static: path.join(__dirname, 'dist'),
port: 3001,
},
output: {
publicPath: 'auto',
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react'],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'app1',
remotes: {
app2: Promise((resolve) => {
const urlParams = new URLSearchParams(window.location.search);
const version = urlParams.get('app1VersionParam');
// This part depends on how you plan on hosting and versioning your federated modules
const remoteUrlWithVersion = '[app2Url]' + '/remoteEntry.js';
const script = document.createElement('script');
script.src = remoteUrlWithVersion;
script.onload = () => {
// the injected script has loaded and is available on window
// we can now resolve this Promise
const proxy = {
get: (request) => window.app1.get(request),
init: (arg) => {
try {
return window.app1.init(arg);
} catch (e) {
console.log('remote container already initialized');
}
},
};
resolve(proxy);
};
// inject this script with the src set to the versioned remoteEntry.js
document.head.appendChild(script);
}),
// "app2@[app2Url]/remoteEntry.js",
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
new ExternalTemplateRemotesPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
I tried start the app1
again, then this error es:
$ webpack serve
[webpack-cli] Failed to load '/home/projects/github-rl5uyr/app1/webpack.config.js' config
[webpack-cli] TypeError: undefined is not a promise
at Promise (<anonymous>)
at Object.eval (/home/projects/github-rl5uyr/app1/webpack.config.js:32:15)
at Object.function (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:11:114831)
at Module._pile (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:167880)
at Object.Module._extensions..js (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:168239)
at Module.load (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:166317)
at Function.Module._load (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:163857)
at Module.require (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:166635)
at i (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:6:427483)
at _0x5301a6 (https://github-rl5uyr.w.staticblitz./blitz.01faa899fac41642342f4b7113feacabea334fa1.js:11:114450)
So, can the module federations run independently each other? If not, what's the real difference as normal library dependencies of monolith front end instead of this sophisticated micro frontend, that I assumed it should be able to work independently like microservices?
Share Improve this question asked Jan 21, 2022 at 4:56 Dhana D.Dhana D. 1,7204 gold badges13 silver badges36 bronze badges3 Answers
Reset to default 1I think there are some stuff to be fixed in that promise definition: first of all it should be defined as a string
app2: new Promise((resolve) => { ....
Where the configuration passed for app2 is a string between ` character
As stated in the docs
After that you should change your proxy definition to fetch window.app2
not window.app1
.
Finally remoteUrlWithVersion = '[app2Url]' + '/remoteEntry.js';
is not a valid URL
Hope it helps
I faced the same problem and the solution I found was not in the webpack.config.js
but in the import of the modules in react. The first thing is to install these 2 dependencies
npm i react-lazily (https://www.npmjs./package/react-lazily)
npm i react-error-boundary (https://www.npmjs./package/react-error-boundary)
The ponents or views that you want to import from any of your apps would be called with lazily
, for example
const { Login } = lazily(() => import("auth/Auth"));
const { Directory } = lazily(() => import("directory/Directory"));
const { Resources } = lazily(() => import("resources/Resources"));
const { Statistics } = lazily(() => import("statistics/Statistics"));
For the use of react-error-boundary
I would remend making a separate ponent, so I did
import React, { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
export const Externals = ({ children }) =>
{
return (
<Suspense
fallback="loading...."
>
<ErrorBoundary>
{ children }
</ErrorBoundary>
</Suspense>
)
}
In my case I made use of this ponent in my AppRouter.jsx
file as follows
import React from 'react';
import { createBrowserRouter, Navigate } from "react-router-dom";
import { lazily } from 'react-lazily'
import { Authentification, AuthentificationChecked } from '../views/grid/index';
import { Externals } from './Externals';
const { Login } = lazily(() => import("auth/Auth"));
const { Directory } = lazily(() => import("directory/Directory"));
const { Resources } = lazily(() => import("resources/Resources"));
const { Statistics } = lazily(() => import("statistics/Statistics"));
export const router = createBrowserRouter(
[
{
path: '/*', element: <Navigate to="auth/login" />
},
{
path: 'auth',
element: <Authentification />,
children:
[
{ path: 'login', element: <Externals><Login /></Externals>, errorElement: <div>Error al cargar el modulo</div> }
]
},
{
path: 'main',
element: <AuthentificationChecked />,
children:
[
{ path: 'directory', element: <Externals><Directory /></Externals>, errorElement: <div>Error al cargar el modulo</div> },
{ path: 'resources', element: <Externals><Resources /></Externals>, errorElement: <div>Error al cargar el modulo</div> },
{ path: 'statistics', element: <Externals><Statistics /></Externals>, errorElement: <div>Error al cargar el modulo</div> }
]
}
]);
Where <Externals />
encapsulates my ponent that I import from any of the apps
, if it responds correctly in the view the ponent will be reflected, but if an error occurs the application that imports it will not hang or stay in white, in its case it will show the error message that is included in errorElement
of each route.
And this without having to do any extra configuration in the webpack.config.js
, this is how my file looks
remotes:
{
auth: "auth@http://localhost:8081/remoteEntry.js",
directory: "directory@http://localhost:8082/remoteEntry.js",
resources: "resources@http://localhost:8083/remoteEntry.js",
statistics: "statistics@http://localhost:8084/remoteEntry.js"
},
Problem: TypeError: Failed to fetch dynamically imported module:
I had this when trying to load an unavailable module while loading it lazily.
const Counter = lazy(() => import("remoteApp/Counter")); // it may break!
I solved it natively on the react
side WITHOUT adding any extra dependencies by catching the error on the import statement
like so:
import { lazy, Suspense } from "react";
const Counter = lazy(() =>
import("remoteApp/Counter").catch(() => ({
default: ({ onLoadingError }: { onLoadingError: (error: Error) => void }) => {
onLoadingError(new Error("Unable to load Counter"));
return <h5>Unable to load Counter, please reload the page!</h5>;
},
}))
);
export default function App() {
function errorHandler(e: Error) {
console.error(e.message);
// Your app is saved from bowing up!
// Do whatever you want here...
}
return (
<>
<h1>Host App</h1>
<div className="card">
<Suspense fallback="Loading Counter...">
<Counter onLoadingError={errorHandler} />
</Suspense>
</div>
</>
);
}
Counter
is a regular react ponent.
If the error occurs it will return a fallback UI in this case:
<h5>Unable to load Counter, please reload the page!</h5>
I passed a custom error handler named onLoadingError
to inform my business logic that the remote ponent failed to load.
本文标签: javascriptWebpack Module Federation handling when the remote app is downunavailableStack Overflow
版权声明:本文标题:javascript - Webpack Module Federation handling when the remote app is downunavailable - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742122649a2421792.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论