I'm following a couple articles on how to implement a simple micro-frontend approach with React ( here and here , see sample repos at bottom of question).
It works perfectly when the two apps (the root app, and the subapp) are running in their respective developoment servers. However, when I deploy the build artifacts to the real web server, it doesn't work. This is the important code in the root app:
function MicroFrontend({name, host, history}) {
useEffect(() => {
const scriptId = `micro-frontend-script-${name}`;
const renderMicroFrontend = () => {
window["render" + name](name + "-container", history);
};
if (document.getElementById(scriptId)) {
renderMicroFrontend();
return;
}
fetch(`${host}/asset-manifest.json`)
.then((res) => res.json())
.then((manifest) => {
const script = document.createElement("script");
script.id = scriptId;
script.crossOrigin = "";
script.src = `${host}${manifest.files["main.js"]}`;
script.onload = () => {
renderMicroFrontend();
};
document.head.appendChild(script);
});
return () => {
window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`);
};
});
return <main id={`${name}-container`}/>;
}
MicroFrontend.defaultProps = {
document,
window,
};
When I click on the button that loads the main.js for the micro-app, it works fine up till the point where it calls the renderMicroFrontend
above. Then I get this error in my browser: Uncaught TypeError: window[("render" + t)] is not a function
This is because it can't find the function that loads the microfrontend that is supposed to be on window
. When I run the two apps in the dev server, I have the correct function on window
and it works. When I follow the same steps with the two apps deployed to a real server (after running npm run build
), instead of having the renderMicroApp
function on my window
, I have a different variable called: webpackJsonpmicro-app
.
I figured out that this is due to the output.library option (and/or related options) in webpack, from the webpack docs:
output.jsonpFunction. string = 'webpackJsonp' Only used when target is set to 'web', which uses JSONP for loading on-demand chunks. If using the output.library option, the library name is automatically concatenated with output.jsonpFunction's value.
I basically want the bundle/script to be loaded and evaluated, so that the renderMicroApp
function is available on the window, but I'm lost regarding what webpack settings I need for this to work, since there are lots of different permutations of options.
For reference, I'm using the following config-overrides in the micro-app (atop react-app-rewired
):
module.exports = {
webpack: (config, env) => {
config.optimization.runtimeChunk = false;
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
config.output.filename = "static/js/[name].js";
config.plugins[5].options.filename = "static/css/[name].css";
config.plugins[5].options.moduleFilename = () => "static/css/main.css";
return config;
},
};
And in my index.tsx (in the micro app) I'm exposing the function on the window:
window.renderMicroApp = (containerId, history) => {
ReactDOM.render(<AppRoot />, document.getElementById(containerId));
serviceWorker.unregister();
};
Some sample repos:
You could try with terser-webpack-plugin .
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
webpack: (config, env) => {
config.optimization.minimize = true;
config.optimization.minimizer = [new TerserPlugin({
terserOptions: { keep_fnames: true }
})];
config.optimization.runtimeChunk = false;
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
config.output.filename = "static/js/[name].js";
config.plugins[5].options.filename = "static/css/[name].css";
config.plugins[5].options.moduleFilename = () => "static/css/main.css";
return config;
}
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.