4

I am building an app using a microfrontends architecture with the Single-Spa framework.

I have:

  • A root config web app: Defines the layout of the apps and includes each microfrontend as an NPM dependency
  • N Microfrontends: React apps which are loaded into the root config app.

Because each microfrontend is using React, I don't want to bundle it everytime. Instead I want to have the root config app specify React as a dependency and then it can be loaded by each microfrontend.

I have declared react and react-dom as webpack externals so they are not bundled but I think this will only work if I include React from CDN in my index.html of the root config app. Is there a way to configure webpack so that it will include React in the root config and make it available to every microfrontend? I can't use public CDNs in my firm.

Thanks!

sam
  • 2,469
  • 8
  • 37
  • 57

3 Answers3

0

In the "Getting Started: Quick start" guide, we cover how to add shared dependencies, and the example specifically shows adding React and ReactDOM to the import map.

You should not try to bundle React into the root-config, since modules bundled into an application by Webpack are namespaced and not shared globally. Instead, per your requirement, you can host those React files in your own CDN or even serve them locally though the root-config because ultimately there are static files. The location of where those files are loaded from is not relevant to SystemJS as long as it can load them. This means you can go as simple as copying those files locally and serving them from the root-config server, to as complicated as setting up your own unpkg server.

filoxo
  • 8,132
  • 3
  • 33
  • 38
  • If react is defined as external in my microfrontend app then it will not be bundled in the microfrontend output JS. If I then add react as a dependency in my root-config package JSON (but don't actually use React at all in the root config app) will Webpack not then make React available such that when my microfrontend imports React, webpack will resolve 'React' from root-config node_modules? If this is the case then what is the advantage of having React in a CDN vs just having it already in the root bundle available to all microfrontends? – sam Feb 24 '21 at 19:19
0

Yes, we can do that not just for the React Package but with all the dependencies and share them between all the micro-frontend apps.

Webpack 5, provides the support of ModuleFederationPlugin in which you can define the list of packages. Example:-

Import Package.json in the Webpack configuration and add following plugin

const packageJson = require('../package.json');

{
   plugins: [
    new ModuleFederationPlugin({
            name: <AppName>,
            filename: 'remoteEntry.js',
            exposes: {
                './App': <PathToFile>,
            },
            shared: {
                ...packageJson.dependencies,
            }
        }),
  ],
}

You can explicitly define only the packages name which you want to share.

Check out more about ModuleFederationPlugin

nishit chittora
  • 974
  • 13
  • 20
0

If you want to have flexibility in micro frontend, that is, if you want each micro frontend to be deployed independently of each other, you can use client-side intgration.

React

You can use a CDN for static files. (image, fonts, pdf, doc) Webpack module federation can be used for client-side integration.

Configuring an example webpack and module federation.

We divide the webpack config files into 3 parts. for common, development, production

webpack.common.js

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'],
          },
        },
      },
    ],
  },
};

webpack.dev.js

const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const commonConfig = require('./webpack.common');
const packageJson = require('../package.json');

const devConfig = {
  mode: 'development',
  output: {
    publicPath: 'http://localhost:8082/',
  },
  devServer: {
    port: 8082,
    historyApiFallback: {
      index: 'index.html',
    },
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'auth',
      filename: 'remoteEntry.js',
      exposes: {
        './AuthApp': './src/bootstrap',
      },
      shared: packageJson.dependencies,
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
};

module.exports = merge(commonConfig, devConfig);