JS Module Federation - here is what you need to know about it! (Part 1)
What is Module Federation
The problem
Bundling JavaScript files for client-side consumption has traditionally been a huge pain. You have to bundle all the source code that you think the app is going to use. This was easier when providing files for a single static page that had a little bit of JS magic as the files were small, you could even serve multiple files to the client, you would often see un-minified un-compressed files being thrown to your browser and it wouldn’t lift an eyelid.
Come modern JavaScript applications using a multitude of libraries and frameworks in a single page application and suddenly the dependency size ballooned. Webpack was at hand to bundle and minify files and dependencies, it also helped us write modern JavaScript before all the major browsers could support it. However, it was not uncommon to see bundled packages several megabytes in size.
So how do we split up these huge files? Luckily the evolution of the JavaScript ecosystem and the flexibility of Webpack has enabled us to create different bundles that we can optimise and load sensibly across our front-end applications. (Advanced Webpack users will also be to do this across backend applications when performance needs to be carefully controlled).
However, all packages needed to be eagerly loaded and many 100’s of kilobytes of JavaScript would need to be loaded upfront even if much of the bundle was never executed, this led to slow-loading pages and bloat.
Code Splitting
Many cited code splitting as a way to approach this, but in practice was very difficult. Advances in Webpack allowed for this via dynamic import syntax.?https://webpack.js.org/guides/code-splitting/
However lazily loading dynamic imports into React Applications was still challenging, the (still) experimental `React.lazy` and `Suspense` features now allows this. It also means that this can be used for dynamically loading split code per route. This is pretty awesome and means that lazy loading of routes or application features can allow for much faster initial loading and mounting times. Keeping package sizes and hopefully removing some of those awkward flashes, slow loading speeds, and avoid everything being loaded into memory. As the user visits each route, then the compounding of loading into memory of JS packages is hard to avoid.
This does seem to solve the problem, but still feels like somewhat of a hack and can become slightly complex when trying to manage what is contained in each dynamic import the order they are loaded and how to split dependencies across them. My understanding is that any shared dependencies would need to be preloaded.
When creating an application within a single team, this is still very manageable. When you want to share routes or individual components across multiple teams this is likely to become messy. It is also not a trivial task to share components across applications.
Here comes Module Federation
So here comes Module Federation a Webpack plugin designed and developed by Zack Jackson. This is not purely a plugin alone, it is a whole new approach to JS application architecture and how to share dependencies across applications in a completely decoupled manner.
领英推荐
We are not just talking about external dependencies, we are talking about complete application composition from remotely served packages, knitted together in a host application. So for the simplest module federated application, you will have 1 host and one remote application. The remote application will declare the publication of itself as a package in the module federation plugin directives in its Webpack config. The host will then declare its intention to consume it and the location of where the remote lives.
Then when the application is run, any remote dependencies are called out for and pulled into the package runtime.
Why is this so great?
Because you can now remotely host your JS dependencies, you can have multiple teams independently release their packages for inclusion without having to version the dependency, push it to an artifice repository and re-release each of your applications.
Imagine that you publish a ‘masthead’ or ‘footer’ across multiple sites. If this was a JS module and you had made updates to its styling or to the options available in the links. You would have 2 options, first, to publish and pull down the dependency and redeploy any consumer, the 2nd is to make a custom mechanism to do pull this in at runtime for you. This could be a complex and sometimes brittle process.
A host application
A host application will be responsible for the composition and serving of the page, it will pull in all of the dependencies and usually do the high-level knitting together of a page, this would include the likes of dependencies, routes, and possibly layout. However, as much logic as needed could be in theory delegated to remote dependencies.
A remote application
The remote application declares what modules it is going to be exposing and the entry point at which this is consumable. The app can expose as many or as few packages or in this case specific components as you decide. Of course, thinking about how to expose these will require some thought and planning, if this becomes too granular you might find this hard to manage long term.
Now we have exposed these packages they can be imported into the host application just as you would import any other JS local dependencies.
Part 1 - Conclusion
Module Federation is basically this, host applications consuming remote packages and the provision of the tooling and configuration that you need to be able to do this in a controlled and flexible manner. I will be following up this blog post with a 2nd telling you about some of the more advanced use cases and features in the next week or so.
Fractional CTO | Engineer | Director
3 年nice article Rich - I've been shaking up that tree too recently (but removing webpack)