module-federation-examples
module-federation-examples copied to clipboard
Can the main application work without remoteEntry in development?
In the development environment, if I have multiple remote modules, starting them at the same time is very slow.
Since my remote modules are only render under some route path(dynamic route), Is there any hack way to make my host application works without remoteEntry.js?
Try placing the script tag for the remoteEntry.js file inside the remote component.
Yes you can “point” to remotes deployed to higher environments or production. We do this all the time
You could also shim the remote to return an empty function for any getter. Take a look at promise new promise syntax on webpack docs. You could resolve a fake object that “exports” default with a null function or something.
You could also use try catch to fallback to something else when the import() fails
Yes you can “point” to remotes deployed to higher environments or production. We do this all the time
You could also shim the remote to return an empty function for any getter. Take a look at promise new promise syntax on webpack docs. You could resolve a fake object that “exports” default with a null function or something.
You could also use try catch to fallback to something else when the import() fails
Thanks, I shared "react" deps with production remoteEntry in my dev environment, But it seems incorrectly use the prod version of react, which caused the error messages to be minimized. How can i avoid that?
Set your dev mode app to have react shared as eager:true to force webpack to use your local copy
Since my remote modules are only render under some route path(dynamic route), Is there any hack way to make my host application works without remoteEntry.js?
Wouldn't it be better if we could do it on all environments without a hack? How can we access the remoteEntry.js only when the user navigates to those paths where a new Microfront end is needed for the first time?
Here we have a similar issue that was closed but it was not clear if there is a solution and what it is: https://github.com/module-federation/module-federation-examples/issues/681
1: Use promise new promise if you feel stuck 2: stop statically importing code, since that will cause webpack to load remotes upfront 3: This sounds like user error, remotes only load if something is calling their import. Simple as that.
Things it might be, babel, some other plugin.
Things to try: delete everything and put this, no babel plugins other than the absolute minumum for react to no fail to build, no webpack plugins beyond the bare minimum for an app to serve, no hot reloading plugins, use the @ syntax right from MFP
const someFuntionNEverCalled = ()=>import('app1/thing')
// prevent tree shaking it
console.log(someFuntionNEverCalled)
Created a new repo with a simplified codebase but the remoteEntry.js files are always loaded ahead, even with the "promise new promise".
Code repo: https://github.com/skypyxis/react-ts-module-federation
Does anyone have a repo where the remoteEntry.js is lazy loaded?
For some reason this still preloads remote entries:
React code:
const CounterAppOne = React.lazy(() => import('app1/CounterAppOne'));
{!showApp1 && <button onClick={() => setShowApp1(true)}>Load App 1</button>}
{showApp1 && (
<React.Suspense fallback={'loading'}>
<div className="box">
<h1>APP-1</h1>
<CounterAppOne />
</div>
</React.Suspense>
)}
webpack:
function lazyLoadRemote(remoteUrl, appName) {
return `promise new Promise(resolve => {
const script = document.createElement('script')
script.src = '${remoteUrl}'
console.log('lazyLoadRemote', script.src);
script.onload = () => {
// the injected script has loaded and is available on window
// we can now resolve this Promise
const proxy = {
get: (request) => window.${appName}.get(request),
init: (arg) => {
try {
return window.${appName}.init(arg)
} catch(e) {
console.log('remote container already initialized', e)
}
}
}
resolve(proxy)
}
// inject this script with the src set to the versioned remoteEntry.js
document.head.appendChild(script);
})`;
}
new ModuleFederationPlugin({
name: 'container',
remotes: {
// app1: 'app1@http://localhost:3001/remoteEntry.js',
// app2: 'app2@http://localhost:3002/remoteEntry.js',
app1: lazyLoadRemote('http://localhost:3001/remoteEntry.js', 'app1'),
app2: lazyLoadRemote('http://localhost:3002/remoteEntry.js', 'app2'),
},
...
Hey @ScriptedAlchemy this issue also seems similar to the one i am having.
I am willing to know when a remote its down, so i am starting with baby steps and trying to use promise new Promise
. But even copy and past the examples, i got the error of window.app undefined.
This is my webpack config:
new ModuleFederationPlugin({
name: "manager",
filename: "remoteEntry.js",
remotes: {
fluxograma: `promise new Promise(resolve => {
// This part depends on how you plan on hosting and versioning your federated modules
const remoteUrl = 'http://localhost:8081/remoteEntry.js'
const script = document.createElement('script')
script.src = remoteUrl
script.onload = () => {
const proxy = {
get: (request) => window.fluxograma.get(request),
init: (arg) => {
try {
return window.fluxograma.init(arg)
} catch(e) {
console.log('remote container error')
console.log(e)
}
}
}
resolve(proxy)
}
// inject this script with the src set to the versioned remoteEntry.js
document.head.appendChild(script);
})`,
},
shared: {
vue: {
singleton: true,
},
},
}),
The code on my component:
<template>
<button type="button" @click="loadRemote">Load Remote</button>
</template>
<script>
export default {
methods: {
loadRemote() {
import("fluxograma/Tree");
},
},
};
</script>
Indeed my http://localhost:8081/remoteEntry.js
is loaded:
But when i try to load the exposed component i got the error:
TypeError: window.fluxograma is undefined
That is being thrown at the catch on "window.fluxograma.init(arg)".
Any clue what it may be?
Init can only be called once. Instead of catching it. Do something like window.remote.didInit = true and just check if it was already initialized or not before calling init again.
Regarding remotes always getting called upfront. I've noticed this as of late - likely a change in webpack startup.
A dirty workaround is to resolve a fake object with no init at all and do everything in the get() so only when a exposed module is used, inject the real remote. Init it, then call get and return the got module.
hey @ScriptedAlchemy i was doing something wrong, like a said on the issue of the enhanced, i've manage to do this work 😅
@schirrel @ScriptedAlchemy The snipped provided by schirrel does not work the way I expected, I presume the opening post matches my use case where I'm seeing lazy loaded components eagerly loaded because of something in the webpack runtime.
In react which I am using when I use:
const Component = React.lazy(() => import('child/App'))
I would expect the "child" remote to not load until Component is rendered into the react tree. Instead I am seeing the child remote initiated by a separate runtime the call stack does not match the typical React.lazy api that is prefetching the remote. I don't think it's blocking the main app, but it's causing modules to be downloading that may never be used. Is this intentional behavior? Is there a way to turn in off and lazy load the component until it is utilized in the react tree like it does in traditional react apps?
This is the initiator call stack from a typical react app:
This is the initiator call stack from a federated module application:
@42shadow42 when you say "I would expect the "child" remote to not load until Component is" you mean the component or the remote entry? The snippet i post loads the remote entry, which is in fact required to load always at the start, it only dont block the app shell startup. Because it must be registred for the webpack runtime. But the component itself will only load when required. The code on my snippet is working on production and behaviors as it
@schirrel I was thinking that the remoteEntry should not be downloaded until it is needed, I have observed that it doesn't execute until necessary, but in large apps it may not be necessary to load all bundles.
@42shadow42 the remoteEntry files are use small kbs, like an interface only and shouldn't be a problem. @ScriptedAlchemy can correct me, but without the remoteEntry the runtime cant know if it is from a remote or a lib, and so to know if is necessary to load another thing or don't.
@schirrel Does this mean that there is another file loaded when the remote gets that includes the actual implementation? If so this is a non-issue. I just didn't understand that remoteEntry wasn't the actual bundle.
Yes @42shadow42. Look at below: my app is using a "cronograma" remote. I have from the start only the remoteEntry load (the second link is because i configured shared wrong). And only when i click to use my remote, the component itself and all its needs is loaded
https://user-images.githubusercontent.com/6757777/184043333-8b6beb89-f759-4abb-9dab-3b16ea53eb8f.mov