Introduce the Module Federation

Module Federation enables JavaScript applications to run code in another JavaScript application dynamically and to share dependencies.

Have to say, as a result of the Internet related resources are limited, in the research process, almost did not climb out of the pit…… Split on the spot 😣. If this article can help you to a certain extent, I hope to give the author a thumbs up duck 😘.

To understand the above statement, we can look at the technology from a practical perspective. In our daily development, we often encounter the need to reuse a piece of code logic scene, we can generally from the following ways to achieve the appeal.

File extraction method

If you only need to reuse under the current project, then the process is very simple and fast, we can create a new JS file, put the code in, and expose it to achieve the requirements.

Although this way is simple and efficient, but its limitations are also obviously, it only supports under the current project, if we want to multiple projects for reuse, we need to copy and paste the code, which is very simple and practical CV solution, this way is undoubtedly the most brutal, it is very likely that, after our project is more and more big Make you cry during subsequent maintenance.

NPM package

For multi-project co-dependency scenarios, it is common to maintain a common NPM package, install and import it in the project that needs to be used. This pattern compensates for the shortcomings of our approach above, enabling the ability to share multiple projects, which is its advantage, and avoiding redundant code.

However, this approach’s most obvious flaw is that every time we modify the public library at the same time, if you want other dependent project you can enjoy new features, so we need to updated versions of these projects and new package, and then implement online packaging process, for large projects and involve complex logic, the price is very expensive.

Having said so much, it’s time for today’s hero.

The Module Federation way

For the problems in the above scenario, let’s talk about this guy’s strengths one by one.

If Module Federation is used, we need to extract the code that needs to be reused into a single file (or not, as long as it is exposed). Then we need to configure Module Federation and export the file. The user can directly reuse the file remotely, and does not care about the dependencies in the reuse file, it will take care of us automatically.

At the same time, if multiple projects use this reusable file, we only need to issue the version of the project providing the service when we make changes to it, and users can obtain the new code in real time.

To put it bluntly, if a project is already live, other projects can directly use the specified JS file in the project without caring about its dependencies.

Here we make a simple comparison of different schemes in the above scenarios:

Js file NPM package Module Federation
Operational complexity simple medium medium
Release complexity complex medium simple
maintainability low high high

Further the Module Federation

From the above scenario, you have an idea of how Module Federation can directly use the specified JS files of remote projects.

Let’s continue to explore the benefits of this technology for our daily business.

Here’s a picture from the unknown giant


As is known to all, in an era of current Vue, the React framework, most of the way we write a variety of components, not only that, but for the maintainability of the project, we will be out of the many common components or method, it is under the demand, has given rise to batch quality third-party component libraries or utils library.

Similarly, these third-party libraries also face the problems we talked about at the beginning. How to release and maintain quickly and conveniently, and how to reduce the burden of users is also a problem worth thinking about.

In some large projects, we often need to install a large number of third-party packages, with the help of Webpack, although we can facilitate the development of the project, but we have to mention that in the case of bad project optimization, it is really possible to change a line of code and wait for a few minutes.

It is precisely because of this defect that the university of Utah and other front-end giants are exploring the way of no Webpack, such as the recent launch of vite 2.0, which has indeed brought a new solution to many of the development suffering from Webpack. From this point of view, is there really no other good way for WebPack to reduce build times than by constantly optimizing?

In the past I would have said Yes, but today I want to say NO, Webpack can save the day!

This article corresponds to the practice project address: project source code address

Clone project experience is recommended.

Module Federation Application scenario

Ability to provide public service

The main thing I want to mention here is its ability to provide remote public components and JS modules.

Well, if we use this capability, we don’t need the component library NPM package, we can just pull out a project to host this capability, all the common components can be directly accessed through a remote link, no installation at all, can be used directly.

Without further ado, on the code:

  <div id="app">
    <h1>Hello Other Vue2</h1>
    <HelloWorld msg="I am a local Vue component"></HelloWorld>
    <HelloWorld2 msg="I am a remote Vue component"></HelloWorld2>

import HelloWorld2 from '@v2hw/HelloWorld'
import HelloWorld from './components/HelloWorld'
export default {
  name: 'App'.components: {
Copy the code

The import HelloWorld2 from ‘@v2hw/HelloWorld’ is used to import a remote component, and then we can register it directly.

Look at the results:

The left side renders the components under the current project, and the right side represents the components fetched and rendered from the remote side. Isn’t that exciting?

With this capability, maintain a 🔨 component library.

Similarly, this remote import capability applies to JS modules.

Why don’t you go to 👻 for repeats

What else can we do with it besides reuse components?

Above the author ruthessly stepped on the construction speed of webpack one foot, this time also should give a sweet jujube to comfort this elder brothers.

Let’s think about it for a moment. Most of the time in our daily business, the largest part of the project volume we develop is our third party package, which we both love and hate. In addition, these third-party packages are not modified, so every build (regardless of the cache), we seem to be reworking the work. Can we use Module Federation’s ability to maintain these third-party packages remotely? We only need to import them in runtime mode.

No sooner said than done:

	<div class="hello">
		<Card hoverable style="width: 240px">
			<CardMeta title="Vue HelloWorld">
				<template slot="description">
					{{ msg }}

import { Card } from '@v2hw/ant-design-vue'
// import { Card } from 'ant-design-vue'
export default {
	name: "HelloWorld".props: {
		msg: String,},components: {
    CardMeta: Card.Meta

Copy the code

Instead of importing Ant-Design-Vue directly from the current project, I’m going to fetch it directly from a remote project to see if it can improve build performance.

The difference between the two methods can not be clearly seen from the code, so we analyze it by their running results (note the content in the red circle) :

] (

As we can see from the figure, the above circle is the build time of remote dependency, the latter is the build time of local third-party package, one is 2713ms, the other is 4695ms, and the speed is improved: * * * * 42% or so, it’s just a third-party packages can bring so much of the increase, usually slightly bigger project the number of third-party packages are very large, if the amount handed over to the far side, so every time we code only need to build our code, without having to care about the third-party packages, this ascension really let a person very excited.

At this time, some author may ask, then I will directly introduce all third parties in the form of CDN, not finished, and you achieve the same effect.

It is true that this method is similar to the introduction of CDN and the integration of Webpack External, but using WebPack Module Federation can control the package in our own hands, we do not have to worry about the day when the CDN link is down, we do not know. And for many third-party packages that don’t offer CDN at all, you won’t be able to achieve the desired effect, and this is just one of the capabilities of WebPack Module Federation.

Detailed configuration mode

ModuleFederationPlugin (ModuleFederationPlugin) we need to configure this plugin to implement our requirements. First, let’s look at the configuration items of this plugin:

The field name type meaning
name string Mandatory value, that is, the name of the output module, which is referenced remotely${name}/${expose}
library object How to declare a global variable. Name is the name of the UMD
filename string Build the file name of the output
remotes object A mapping of remotely referenced application names and their aliases, using the key value as the name
exposes object The resource path and its alias that can be exposed when referenced remotely
shared object Third party dependencies that can be shared with other applications so that you don’t have to load the same dependencies twice in your code

With these we can practice in real life:

One thing to note here is that in the ModuleFederationPlugin world, all projects can be either a provider or a user of a remote component, so only a single provider and user are shown here for the sake of distinction.

Component Provider

We want to use a remote component, so we need to configure the provider of the remote component.

For ease of interpretation, I have removed other unnecessary code here, leaving only the use of the ModuleFederationPlugin.

const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')

module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: 'vue2Project'.filename: 'hello-world.js'.exposes: {
                './HelloWorld': path.resolve(projectDir, './src/components/HelloWorld'),
                './ant-design-vue': path.resolve(projectDir, './src/utils/ant-design-vue')},shared: [vue,'ant-design-vue']]}})Copy the code

Start by defining the name and filename attributes, which are key and will be used when using remote components.


The meaning of the name is exposed to external use. The authors have exposed two components here, a regular Helloworld.vue component and an Ant-Design-Vue third-party package, the service provider using the remote third-party package we demonstrated earlier, SRC /utils/ant-design-vue: SRC /utils/ant-design-vue: SRC /utils/ant-design-vue: SRC /utils/ant-design-vue: SRC /utils/ant-design-vue

// src/utils/ant-design-vue
export * from 'ant-design-vue';
Copy the code

Here’s a little interpretation of the usage in this object:

  • Property name: usually use./ XXX, define that we expose the name of the component, and then write the variable name/XXX when using it, as shown in the previous import method.

    • import { Card } from '@v2hw/ant-design-vue'
      Copy the code
  • Property value: Represents the actual location of this component.


This property is also interesting because when we use a remote component, if the component depends on a third-party package, it will first look for the shared component of the user, if found, it will directly use the current project, otherwise it will fetch the remote component from the provider.

Component user

When our provider project is started, we can easily use it in another project:

Import file modification

For general projects, our entry file is index.js or main.js in the SRC directory, as well as others, and this entry file does an initialization of the relevant framework or library, such as the following entry file for the Vue project:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h= > h(App),

Copy the code

Here is a basic example of how to instantiate a Vue. What should we do when using Module Federation?

Create a new bootstrap.js file in the same directory (depending on your preference), transfer the contents of the original import file to this file, and then import and execute the file in the original import file:

/ / index. Js or main. Js
Copy the code

So we can keep going.

Here is not careful to fall in, it is a huge pit, at that time because the author did not move this entry file, along the way did not get out, the official website also nothing 😭.

This is a dependency front concept of Module federation. If it is implemented synchronously, there is no guarantee that the dependency modules will be ready when the project is started. Therefore, we use the import method to implement async implementation, and then webPack will analyze the dependencies for us. Wait for the pre-dependencies to load before executing the relevant content in bootstrap to start the project.

The pre-dependencies referred to here are the dependencies that our current project uses with Module Federation to use remote components.

Webpack configuration
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')

module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: 'vue2TwoProject'.filename: 'hello-world.js'.exposes: {
                './HelloWorld': path.resolve(projectDir, './src/components/HelloWorld')},remotes: {
                '@v2hw': 'vue2Project@http://localhost:5001/hello-world.js'
            shared: {
                vue: {
                    import: "vue".shareKey: "vue".shareScope: "default".singleton: true}}})]}Copy the code

I won’t mention any of the other attributes, but I’ll focus on remotes.


The attribute name represents the alias (globally unique value) of the reference. The attribute value consists of several parts: {name}@{url}/{filename} :

  • Name: The name of the remote project, which is the remote component providerModuleFederationPluginThe configuration of thenameField.
  • Url: The address of the remote project
  • Filename: indicates the remote component providerModuleFederationPluginThe configuration of thefilenameField.

Now you can use it directly in your project:

import { Card } from '@v2hw/ant-design-vue'
Copy the code

The @v2hW remote project exposes the Ant-Design-vue property, so we can get it directly this way.

In addition to the above configuration, you can also use the library configuration method to use remote components. The difference is that we need to import remote project js files in the project. I prefer this direct configuration method to introduce script tags. Those who are interested can go to the official website.

Look behind you for ways to dynamically import remote components.

Qi Yin skills

Now that our project has the ability to use remote components from other projects, isn’t that… (Hey, hey, hey) How about interworking components between different frameworks? For example, Vue3 uses the Vue2 component, or Vue uses the React component?

Just think about it. Try it.

Vue3 uses the Vue2 component

Through the story in front of, I believe everyone to remote components import should also have a certain understanding, in the same version of the Vue project, we can be registered using components imported directly, and for different versions of the Vue component, we need to do the ability of an adapter, is incompatible components into compatible, Here’s how to make the Vue2 component usable in Vue3:

  <div id="vue2HW"></div>

import { vue2ToVue3 } from './utils'
import HelloWorld2 from '@v2hw/HelloWorld'

export default {
  name: 'App'.components: {
    Vue2HelloWorld: vue2ToVue3(HelloWorld2, 'vue2HW')}}</script>
Copy the code

Seen from the code, and before Vue2 projects using Vue2 remote component is not the same, here in registered call a method at the same time, from the perspective on the name of this method we can probably know that this is a Vue3 Vue2 components can be converted to able to identify the structure of the method, then let’s take a look at this method specific to do what.

Vue adapter

Before diving into the detailed code, let’s take a look at what the component data we imported above looks like (note the console.log of the code above) :

Vue2 Options = Vue2 Options = Vue2 Options = Vue2 Options

import Vue2 from './vue2';

export function vue2ToVue3(WrapperComponent, wrapperId) {
    let vm;
    return {
        mounted() {
            vm = new Vue2({
                render: createElement= > {
                    return createElement(
                            on: this.$attrs, Vue3 has merged '$listeners' and' $attrs' of Vue2 into '$listeners' of Vue2
                            attrs: this.$attrs,
                            props: this.$props,
                            scopedSlots: this.$scopedSlots
            vm.$mount(` #${wrapperId}`)},props: WrapperComponent.props,
        render(){ vm && vm.$forceUpdate(); }}}Copy the code

What we need to know is that our Vue component template will eventually be compiled into a render function to provide rendering capabilities, so we need to convert the Vue2 component data we obtained remotely into a Vue3 component compatible structure to ensure that Vue3 can correctly parse this component.

< div style = “max-width: 100%; clear: both; max-width: 100%; So update the vue2 instance we created. The First step is to define a VM variable that will receive the instantiated Vue object later. Then let’s look at the Mounted function and focus on what this function does.

Here we import a Vue2 version of the package, and then use this package to render the Vue2 component. Internally we use the render function to call the createElement method to render, and pass the attributes that need to be passed to this component. The on attribute is also passed in as Vue3’s $attrs attribute.

Then, after rendering is complete, mount the object manually by calling $mount to the ID on the DOM passed in.

In this way, a simple Vue2 to Vue3 is completed, is not also very simple.

Dynamic introduction

Before we use the remote component in the use of configuration mode, this way in some need to be flexible dynamic import is not convenient, next show the dynamic component import mode:

Next, the Vue3 project will be used as a demo. It doesn’t matter if you don’t know much about it.

  <div id="vue2Remote"></div>

import { defineAsyncComponent } from 'vue'
import { vue2ToVue3, loadRemoteComponent } from './utils'

export default {
  name: 'App'.components: {
    dynamicHelloWorld: defineAsyncComponent(async() = > {const component = await loadRemoteComponent({
            url: 'http://localhost:5001/hello-world.js'.scope: 'vue2Project'.module: './HelloWorld'
        return vue2ToVue3(component.default, 'vue2Remote'); }}})</script>
Copy the code

Here we use Vue3’s defineAsyncComponent method to register asynchronous components, which is asynchronous because dynamically fetching a remote component involves a request for the remote component.

And the core of this is the loadRemoteComponent method, which takes three arguments:

  • Url: Remote project address.
  • Scope: Name of the remote project.
  • Module: Specifies the module in the remote project.

LoadRemoteComponent loadRemoteComponent loadRemoteComponent loadRemoteComponent

export async function loadRemoteComponent(config) {
    return loadScript(config).then(() = > loadComponentByWebpack(config))

function loadScript(config) {
    return new Promise((resolve, reject) = > {
        const script = document.createElement('script');
        script.src = config.url
        script.type = 'text/javascript'
        script.async = true
        script.onload = () = > {
            console.log(`Dynamic Script Loaded: ${config.url}`)
        script.onerror = () = > {
            console.error(`Dynamic Script Error: ${config.url}`)

async function loadComponentByWebpack({ scope, module }) {
    // Initializes the shared scope, which will populate it with this build and all known modules provided remotely
    await __webpack_init_sharing__('default')
    const container = window[scope] // Get the container
    // Initializes the container, which can provide shared modules
    await container.init(__webpack_share_scopes__.default);
    const factory = await window[scope].get(module);
    return factory();
Copy the code

Let’s look at the loadRemoteComponent method, which internally returns the data retrieved from the remote component. Look down at the loadScript method, which returns a Promise, Load a remote JS file using javascript to dynamically create a script tag. When the load is complete, remove the tag from the page and end.

LoadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack: loadComponentByWebpack Returns the corresponding module from the related scope.

The Vue2 component is introduced in React

Similarly, this process also requires an Adapter, so instead of writing one myself, I use vuera to achieve this effect.

This package supports React with Vue as well as Vue with React.

import React, { useState } from "react";
import { VueWrapper } from "vuera";
import VueHelloWorld from "@v2hw/HelloWorld";
import styled from 'styled-components'
import { Card } from "antd";
const { Meta } = Card;

const AppDiv = styled.div` display: flex; justify-content: space-around; `

const ReactContainer = styled.div` margin: 10px; display: inline-block; width: 240px; height: 400px; `

function App() {
	return (
					style={{ width: 240 }}
						title="Hello World!"
						description="I'm the React component."
			<VueWrapper component={VueHelloWorld}></VueWrapper>

export default App;

Copy the code

See the effect

The principle of analyzing

Project source code address

Clone the project locally, go to the module-federation directory (vue2 and vue2-two subprojects from that directory are demonstrated here), and then execute the build command (see package.json for details).

  1. npm run build:vue2:devoryarn build:vue2:dev
  2. npm run build:vue2:two:devoryarn build:vue2:two:dev

After executing the above commands, we have the build results of the two subprojects (output to the corresponding directory in the dist directory). We give the two projects a name:

  • Project A:vue2
  • B of the project:vue2-two

The dependencies are: Project B uses module Federation to use A remote component exposed by project A, so A is the provider and B is the consumer.

Let’s start by looking at the source code for project B, the consumer built product for remote components or libraries (I’ve removed some of the complex code here for ease of understanding) :

// dist/vue2-two/hello-world.js

/ / rely on the chunk and its internal dependencies, here said the ant - design - we use vue from webpack/container/remote / @ v2hw/ant - design - vue
var chunkMapping = {
	"webpack_container_remote_v2hw_ant-design-vue": [
		"webpack/container/remote/@v2hw/ant-design-vue"]};// All dependencies of the corresponding module
var idToExternalAndNameMapping = {
	"webpack/container/remote/@v2hw/ant-design-vue": [
		"default"."./ant-design-vue"."webpack/container/reference/@v2hw"]}; __webpack_require__.f.remotes =(chunkId, promises) = > {
    // Check whether the chunk to be loaded is declared in remotes in the module-federation configuration item, i.e. in chunkMapping above
	if(__webpack_require__.o(chunkMapping, chunkId)) {
        // Find other modules that the chunk depends on according to the mapping of the currently loaded module in chunkMapping
		chunkMapping[chunkId].forEach((id) = > {

            / / based on the need to load modules in idToExternalAndNameMapping mapping, needed to find other dependencies
			var data = idToExternalAndNameMapping[id];

			var onFactory = (factory) = > {
				data.p = 1;
				__webpack_modules__[id] = (module) = > {
					module.exports = factory(); // Return the contents of the loaded module}}; }); }}Copy the code

Combined with the source notes, we can find that the whole process is roughly as follows:

Import a remote module => Obtain the actual source of the module => Obtain all its dependencies by the id of the module => Obtain the final result


Through this introduction, I believe you also to the Module Federation have a general understanding to the technology, if you are interested in, it could dig deep mining of other interesting mechanics, the author in the process of exploring feel the potential of the technology, in the future, maybe it will be the ultimate solution, micro front-end A scheme that naturally supports remote component invocation.

Whisper BB: Fully embrace Module Federation, ditch microfrontend, ditch iframe, webpack yes.

Refer to the link

Explore the new webpackage 5 feature Module Federation in Tencent document application

The official documentation

YY team emp

Take a look at Webpack Module Federation

Three application scenarios research, Webpack new function Module Federation in-depth analysis