import() other modules from third parties, or other webpack builds themselves while sharing dependencies! At runtime! Welcome To Webpack Module Federation
This Project has been incoporated into the Webpack 5 core. Track the progress and share the issue for wider exposure. I believe a system like this would offer great benefits for the JavaScript community. https://github.com/ScriptedAlchemy/webpack/issues/10352
Because this project is now based out of the Webpack 5 repo. It serves mostly as an example, testing ground, and documentation house.
-
Run an install from the root
yarn install
-
From the root of the project, run
yarn dev
Configure each webpack build you intend to federate code between.
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// other webpack config options...
output: {
publicPath: 'http://localhost:3002/', //important to specify url path if loading code from alternative domains/ports
},
plugins: [
new ModuleFederationPlugin({
name: 'website1',
library: { type: 'var', name: 'website1' },
filename: 'remoteEntry.js',
exposes: {
Footer: './src/Footer',
},
remotes: {
website2: 'website2',
},
shared: ['react', 'react-dom'],
}),
],
};
Webpack will generate a special file along with a standard build. In this
example, i have called it remoteEntry.js
Add the remote entry to an application that will consume federated modules.
<html>
<head>
<script src="http://localhost:3002/remoteEntry.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
Consume code from a remote using any require syntax.
import React, { lazy, Suspense, useState } from 'react';
import Footer from './Footer';
import Footer2 from 'website2/Footer'; // federated
const Title = lazy(() => import('website2/Title')); // federated
export default () => {
return (
<>
<Suspense fallback={'fallback'}>
<Title />
</Suspense>
<p>
This app loads the heading above from website2, and doesnt expose
anything itself.
</p>
<Footer />
<Footer2 />
</>
);
};
name | Must be a unique name, used as the namespace to reference federated code. No two apps should share the same namespace |
---|---|
library | Expects an object, the type can be any of the following. `{type:'var' |
filename | Name of the generated javascript file. The name will be static (no hash) in order to allow orchestration between webpack builds. |
exposes | Expects an object/array. The key is how a module will be required from another app, the value is a relative path based on the context of the build. |
remotes | A list of other remote names. A remote name is the string used in the name option. Its used to inform Webpack of the scope where a module is located |
shared | Object/Array of requests or modules that should be shared between federated code. A remote will depend on the host's dependency, if none exists, the remote will fallback and load its own |
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'container',
library: { type: 'commonjs-module' },
filename: 'container.js',
remotes: {
containerB: '../1-container-full/container.js',
},
shared: ['react'],
}),
],
};
Inside the App, remotes can be consumed as such.
import React from "react";
import ComponentC from "containerB/ComponentC";
export default () => {
return `App rendered with [${React()}] and [${ComponentC()}]`;
};