Integrating Module Federation with Angular CLI promotes a modular development strategy, enabling dynamic code loading and sharing among various Angular projects. This guide thoroughly explores the necessary steps for this integration, with assistance from the @angular-architects/module-federation plugin.
@angular-architects/module-federation plugin installed in your project, as it enhances Angular CLI's capabilities to accommodate Module Federation.To begin utilizing Module Federation, it's imperative to configure the Angular CLI to incorporate Module Federation during the build phase. Due to the CLI's encapsulation of Webpack, the introduction of a custom builder is required to unlock Module Federation's potential.
Provided by the @angular-architects/module-federation package, this custom builder facilitates the integration process. Start by using the ng add command to incorporate it into your projects:
For those utilizing Nx, the procedure deviates slightly. First, execute an npm install to acquire the library, followed by invoking the init schematic:
The --type argument, introduced in version 14.3, meticulously ensures that only the requisite configuration is generated.
In our representation, the project labeled 'shell' encapsulates the code for the shell, whereas 'mfe1' denotes Micro Frontend 1. The execution of the aforementioned commands orchestrates several pivotal tasks:
Skeleton Generation:
A rudimentary webpack.config.js skeleton is crafted to facilitate Module Federation integration.
Custom Builder Installation:
A custom builder is installed, empowering Webpack within the CLI to utilize the generated webpack.config.js.
Port Assignment:
A distinct port is allocated for the ng serve command, enabling simultaneous serving of multiple projects.
It's imperative to acknowledge that the generated webpack.config.js is a partial configuration solely dedicated to governing Module Federation. The remaining configurations are autonomously generated by the CLI, maintaining the usual workflow intact.
This setup lays down a robust foundation for harnessing Module Federation, enabling seamless code sharing and dynamic loading across your Angular CLI projects.
In the realm of Module Federation, the Shell, also referred to as the Host, serves as a pivotal point of integration. This segment delineates the configuration of the Shell to support the lazy-loading of a FlightModule through routing.
Commence by defining the application routes, specifying a lazy-loaded FlightModule via a virtual path:
In this configuration, the path 'mfe1/Module' serves as a virtual representation, indicating that it does not physically exist within the Shell application. Instead, it acts as a reference to a module located in a separate project.
To satisfy the TypeScript compiler, it's necessary to create a type definition for the virtual path:
This declaration guides the TypeScript compiler in understanding the virtual path, easing the import process.
Further, instruct Webpack to resolve all paths prefixed with mfe1 to a remote project. This is achieved within the webpack.config.js file, generated earlier:
In the remotes section, the path mfe1 is mapped to the remote micro-frontend, specifically to its remote entry point. This remote entry, generated by Webpack, contains crucial information for interacting with the micro-frontend.
For development purposes, hardcoding the remote entry's URL is sufficient. However, a dynamic approach is necessary for production environments. The concept of dynamic remotes is further explored in a dedicated documentation page on Dynamic Remotes.
shared property specifies the npm packages to be shared between the Shell and the micro-frontend(s). By using the shareAll helper method, all dependencies listed in your package.json are shared. While this facilitates a quick setup, it may lead to an excessive number of shared dependencies, which could be a concern for optimization.singleton: true and strictVersion: true settings instructs Webpack to throw a runtime error if there is a version mismatch between the Shell and the micro-frontend(s). Changing strictVersion to false would instead result in a runtime warning.requiredVersion: 'auto' option, provided by the @angular-architects/module-federation plugin, automatically determines the version from your package.json, helping to prevent version-related issues.The Micro-frontend, referred to as the Remote in the context of Module Federation, follows a structure similar to a standard Angular application. It includes specific routes within the AppModule and contains a FlightsModule for handling flight-related functionalities. This section details the steps to ensure the FlightsModule is seamlessly loaded into the Shell (Host).
Begin by establishing the basic routes within the AppModule:
This simple routing setup navigates to a HomeComponent when the application is accessed.
Proceed to create a FlightsModule to handle flight-related operations:
This module contains a route to a FlightsSearchComponent defined as follows:
To enable the loading of FlightsModule into the Shell, it's imperative to expose it through the Remote's Webpack configuration:
In this configuration:
name property identifies the micro-frontend as mfe1.exposes property signifies the exposure of FlightsModule under the public name Module, allowing its consumption by the Shell.shared property lists the libraries to be shared with the Shell, using the shareAll method to share all dependencies found in your package.json. The singleton: true and strictVersion: true properties ensure that a single version of shared libraries is used, and a runtime error is triggered in case of version incompatibility, respectively.Having set up the Shell (Host) and Micro-frontend (Remote), it's time to test the configuration to ensure the seamless integration of Module Federation.
Kickstart both the Shell and micro-frontend using the following commands:
Upon executing these commands, the Shell and Micro-frontend will be served, and the respective applications will open in your default web browser.
Navigate to the Flights section in the Shell, and observe the Micro-frontend being dynamically loaded.
The plugin also installs a handy npm script run:all during the ng-add and init schematics, allowing for simultaneous serving of all applications:
For serving selected applications, append their names as command line arguments:
Delving into the main.ts file, you might notice a slight deviation from the usual:
The standard code typically found in main.ts has been migrated to a newly created bootstrap.ts file. This strategic move, orchestrated by the @angular-architects/module-federation plugin, enhances the Module Federation's ability to make informed decisions about library versions at runtime. The use of asynchronous dynamic imports enables Module Federation to accurately determine and load the correct versions of shared libraries.
While the initial setup with shareAll offers a straightforward and operational configuration, it may lead to the creation of unnecessarily large shared bundles. For a more refined approach to managing shared dependencies, it's advisable to shift from using shareAll to employing the share helper. This allows for more precise control over which dependencies are shared:
In this configuration, the share helper allows for explicit sharing of selected packages, enabling a more optimized bundle sharing, and a potential reduction in the load times.