Setting Up Module Federation in a Vue 2 Application
Overview
This documentation provides a step-by-step guide on how to set up Module Federation in a Vue 2 application. This guide assumes that you are familiar with the basics of Module Federation and Vue 2.
Prerequisites
Two pre-configured Vue 2 projects. For assistance with Vue project setup, consult the Vue documentation.
Installing Development Dependencies
To properly configure Webpack for Module Federation, you'll need to add some development dependencies to both of your Vue 2 projects.
Run the following command to add the required dev dependencies:
yarn add webpack webpack-cli webpack-dev-server vue-loader url-loader sass-loader mini-css-extract-plugin html-webpack-plugin dart-sass css-loader -D
After installing the dependencies, add a start script to your package.json file. This script will use Webpack-CLI to start the server. The purpose of this script is to leverage Webpack-CLI for server initialization, rather than using vue-cli
.
In your package.json, update the scripts section as follows:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"start": "webpack-cli serve",
"lint": "vue-cli-service lint"
},
Webpack Configuration
Create a webpack.config.js
file in the root directory of both projects.
Basic Configuration
The webpack.config.js
file in each application must include the following code, with distinct port variables assigned to the two projects.
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const port = 8080;
module.exports = {
mode: "development",
cache: false,
devtool: "source-map",
target: "web",
optimization: {
minimize: false
},
entry: path.resolve(__dirname, "./src/main.ts"), // <1>
output: { // <2>
path: path.resolve(__dirname, "./dist"),
publicPath: `http://localhost:${port}/`
},
resolve: { // <3>
extensions: [".ts", ".js", ".vue", ".json"],
alias: {
vue: "vue/dist/vue.runtime.esm.js",
"@": path.resolve(__dirname, "./src"),
"@assets": path.resolve(__dirname, 'src/assets')
}
},
module: {
rules: [
{ test: /\.vue$/, loader: "vue-loader" },
{
test: /\.ts$/,
loader: "ts-loader",
options: { appendTsSuffixTo: [/\.vue$/] }
},
{
test: /\.css|.sass|.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false
}
},
"css-loader",
"sass-loader"
]
},
{
test: /\.png$/,
use: {
loader: "url-loader",
options: {
esModule: false,
name: "[name].[ext]",
limit: 8192
}
}
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
}),
],
devServer: {
static: {
directory: path.join(__dirname, "public")
},
compress: true,
port,
hot: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
},
},
};
- Entry Point: The entry point is the file that Webpack uses as the starting point for bundling. In Vue applications, this is usually
src/main.ts
.
- Output: Specify the directory where the bundled application should be placed. By default, Vue applications are built into the
dist
directory.
- Resolve: This section is for setting module resolution and aliases. For example, you can add a shortcut to the assets folder by adding the @assets property in resolve.alias.
- Rules: Webpack by default understands only JavaScript and JSON files. Loaders enable Webpack to understand other file types like TypeScript, Vue, CSS, etc. We have added loaders for these types in our configuration.
- Plugins: Plugins serve to tailor the Webpack build process through various customization options.
- devServer: This section contains configurations for the development server, including the port number and other options.
Upon executing the yarn start
command, the server should initialize successfully.
Debugging
After completing the webpack.config.js
configuration, navigate to http://localhost:${port}
. You may notice an empty page without the application's logo, indicating server issues. To resolve this, update the dynamic variables in the public/index.html
file as shown below:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="./favicon.ico">
<title>micro-main</title>
</head>
<body>
<noscript>
<strong>We're sorry but micro-main doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
If the page remains blank, inspect the console for potential errors related to environment variables in the router.
To address this, add a new plugin to your webpack.config.js
to enable the use of process.env
. Update the plugins
array as follows:
const webpack = require('webpack');
// Existing webpack.config.js content
module.exports = {
// ...
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
// Other existing plugins
],
// ...
};
After implementing these changes, reload your application. The server and application should now function as expected.
Implementing Module Federation
Upon successfully setting up Webpack and initializing your development servers, the next step is to implement Module Federation.
Configuration for web_common
Project
The ModuleFederationPlugin
is central to setting up Module Federation. Below is an example configuration for the web_common
project:
// Existing imports and configurations
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... existing configurations
plugins: [
new ModuleFederationPlugin({
name: "web_common",
remotes: {},
filename: "remoteEntry.js",
exposes: {
"./HelloWorld": "./src/components/HelloWorld.vue"
},
// ... other configurations
}),
// ... other existing plugins
],
// ... existing configurations
};
Key Points:
- Import
ModuleFederationPlugin
from require("webpack").container
, not directly from require("webpack")
.
- The
exposes
object is responsible for sharing code from the project. The keys should be in the format ./[KEY_NAME]
.
- The
filename
can be any name, though remoteEntry
or web_commonRemoteEntry
are recommended.
- The
name
can also be any name, but it's advisable to use the name of the project.
Configuration for micro_main
Project
Here is an example configuration for the micro_main
project:
// Existing imports and configurations
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... existing configurations
plugins: [
new ModuleFederationPlugin({
name: "micro_main",
remotes: {
web_common: "web_common@http://localhost:8082/remoteEntry.js"
},
// ... other configurations
}),
// ... other existing plugins
],
// ... existing configurations
};
Key Points:
- The
remotes
object is where you specify projects from which you are receiving shared code.
- The
web_common
is the name of the project from which code is being shared.
- The remote format should be
[PROJECT_NAME]@[PROJECT_URL]/[PROJECT_filename]
. In this case, the filename is remoteEntry.js
.
By following these configurations, you should be able to successfully implement Module Federation in your Vue 2 projects.
Testing the Module Federation Implementation
Importing the Component Globally
To validate the Module Federation setup, import the HelloWorld component globally in your main project.
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.component("HelloWorld", () => import("web_common/HelloWorld"));
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
TypeScript Configuration
If your project uses TypeScript, you'll need to declare web_common
as a module. Add the following declarations:
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
declare module "web_common/*";
Upon completing these steps, your server should operate without issues.
Using the HelloWorld Component
You can now utilize the HelloWorld
component in your main project as shown below:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component({
})
export default class Home extends Vue {}
</script>
Handling 404 Errors
If you encounter 404 errors when navigating to different routes and reloading the page, you can resolve this by enabling historyApiFallback
in your webpack.config.js
:
// webpack.config.js
module.exports = {
// ...
devServer: {
historyApiFallback: true,
},
};