preface
Although Webpack5 has been released for some time, but has not been studied, recently just do micro front-end related research, just saw the webpack5 module federation and micro front-end related programs, they want to explore the relevant source code of the module federation. (ps: on the front, a little way, personal feel during selecting the front-end solutions can be combined with the existing resources and form related trade-off, from the Shared capacity, isolation mechanism and routing authentication data solutions, different dimensions, such as comprehensive consideration, the personal use of the minimum cost of migration, gradual transition, is the optimal choice.)
The directory structure
- container
- ModuleFederationPlugin. Js (core, the key analysis)
- Options.js (user-typed options)
- ContainerEntryDependency.js
- ContainerEntryModule.js
- ContainerEntryModuleFactory.js
- ContainerExposedDependency.js
- Containerplugin.js (core, focus analysis)
- ContainerReferencePlugin. Js (core, the key analysis)
- FallbackDependency.js
- FallbackItemDependency.js
- FallbackModule.js
- FallbackModuleFactory.js
- RemoteModule.js
- RemoteRuntimeModule.js
- RemoteToExternalDependency.js
- sharing
- Shareplugin. js (core, key analysis)
- ShareRuntimeModule.js
- utils.js
- resolveMatchedConfigs.js
- ConsumeSharedFallbackDependency.js
- ConsumeSharedModule.js
- ConsumeSharedPlugin.js
- ConsumeSharedRuntimeModule.js
- ProvideForSharedDependency.js
- ProvideSharedModule.js
- ProvideSharedModuleFactory.js
- ProvideSharedPlugin.js
- The Module of the Module. Js (webpack)
- Modulegraph.js (Module diagram dependencies)
The source code parsing
Overall webpack5 Module, the Module of the Federation is based on ModuleFedreationPlugin js, the final is in the form of webapck plug-in access webpack, ContainerReferencePlugin is used to determine the call relationship between two or more different Containers. SharePlugin is the implementation of the sharing mechanism. Modules are consumed and supplied via ProviderModule and ConsumerModule
Module
Webpack module integration of different modules, erase different differences, module federation is based on Webpack module implementation of dependency sharing and transfer
class Module extends DependenciesBlock { constructor(type, context = null, layer = null) { super(); // The type of the module this.type = type; // The context of the module this.context = context; // this. Layer = layer; this.needId = true; // module id this.debugId = debugId++; Get id() {} set id(value) {} // Module hash, Get hash() {} get renderedHash() {} // Get file get profile() {} set profile(value) {} // Module entry order values Get index() {} set index(value) {} Get index() {} set index(value) {} Get Index2 () {} set index2(value) Get depth() {} set depth(value) {} addChunk(chunk) {} removeChunk(chunk) {} isInChunk(chunk) {} GetChunks () {} getNumberOfChunks() {} get chunksIterable() {} serialize(context) {} deserialize(context) {} {}}Copy the code
ContainerPlugin
class ContainerPlugin { constructor(options) {} apply(compiler) { compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => { const dep = new ContainerEntryDependency(name, exposes, shareScope); dep.loc = { name }; compilation.addEntry( compilation.options.context, dep, { name, filename, library }, error => { if (error) return callback(error); callback(); }); }); compiler.hooks.thisCompilation.tap( PLUGIN_NAME, (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set( ContainerEntryDependency, new ContainerEntryModuleFactory() ); compilation.dependencyFactories.set( ContainerExposedDependency, normalModuleFactory ); }); }}Copy the code
The core of the ContainerPlugin is to load and export the modules of the container, so that there is a layer of packaging on the outside of the module for module transfer and dependency analysis
ContainerReferencePlugin
class ContainerReferencePlugin { constructor(options) {} apply(compiler) { const { _remotes: remotes, _remoteType: remoteType } = this; const remoteExternals = {}; new ExternalsPlugin(remoteType, remoteExternals).apply(compiler); compiler.hooks.compilation.tap( "ContainerReferencePlugin", (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set( RemoteToExternalDependency, normalModuleFactory ); compilation.dependencyFactories.set( FallbackItemDependency, normalModuleFactory ); compilation.dependencyFactories.set( FallbackDependency, new FallbackModuleFactory() ); normalModuleFactory.hooks.factorize.tap( "ContainerReferencePlugin", data => { if (! data.request.includes("!" )) { for (const [key, config] of remotes) { if ( data.request.startsWith(`${key}`) && (data.request.length === key.length || data.request.charCodeAt(key.length) === slashCode) ) { return new RemoteModule( data.request, config.external.map((external, i) => external.startsWith("internal ") ? external.slice(9) : `webpack/container/reference/${key}${ i ? `/fallback-${i}` : "" }` ), `.${data.request.slice(key.length)}`, config.shareScope ); }}}}); compilation.hooks.runtimeRequirementInTree .for(RuntimeGlobals.ensureChunkHandlers) .tap("ContainerReferencePlugin", (chunk, set) => { set.add(RuntimeGlobals.module); set.add(RuntimeGlobals.moduleFactoriesAddOnly); set.add(RuntimeGlobals.hasOwnProperty); set.add(RuntimeGlobals.initializeSharing); set.add(RuntimeGlobals.shareScopeMap); compilation.addRuntimeModule(chunk, new RemoteRuntimeModule()); }); }); }}Copy the code
The core of ContainerReferencePlugin is to achieve module communication and transfer, through the call feedback mechanism to achieve the transfer between modules
sharing
class SharePlugin { constructor(options) { const sharedOptions = parseOptions( options.shared, (item, key) => { if (typeof item ! == "string") throw new Error("Unexpected array in shared"); /** @type {SharedConfig} */ const config = item === key || ! isRequiredVersion(item) ? { import: item } : { import: key, requiredVersion: item }; return config; }, item => item ); const consumes = sharedOptions.map(([key, options]) => ({ [key]: { import: options.import, shareKey: options.shareKey || key, shareScope: options.shareScope, requiredVersion: options.requiredVersion, strictVersion: options.strictVersion, singleton: options.singleton, packageName: options.packageName, eager: options.eager } })); const provides = sharedOptions .filter(([, options]) => options.import ! == false) .map(([key, options]) => ({ [options.import || key]: { shareKey: options.shareKey || key, shareScope: options.shareScope, version: options.version, eager: options.eager } })); this._shareScope = options.shareScope; this._consumes = consumes; this._provides = provides; } apply(compiler) { new ConsumeSharedPlugin({ shareScope: this._shareScope, consumes: this._consumes }).apply(compiler); new ProvideSharedPlugin({ shareScope: this._shareScope, provides: this._provides }).apply(compiler); }}Copy the code
The whole module of Sharing is realizing the function of sharing, which uses the Provider to provide, Consumer to consume the mechanism, the shared data isolation in a specific shareScope, ResolveMatchedConfigs filters provider and consumer dependencies so that the shared dependencies are requested only once
conclusion
The module federation of WebPack5 is to realize the processing of each different module by defining the Container Container. The Container Reference acts as the host to schedule the Container, and each Container asynchronously schedules the exposed modules. For the shared part and request content provided by the provider, each Module has a corresponding Runtime mechanism, which will call the runtime in Consumer for loading after analyzing the call relationship and dependency relationship between modules, and the shared code does not need to be manually packaged. Module federation of WebAPCK5 can realize the mutual call between modules of micro-front-end application, and its sharing and isolation balance is also well controlled. For students who want to study module federation to achieve micro-front-end, they can refer to this article [issue 2154] EMP micro-front-end solution. With the promotion of WebPack5 and the follow-up of major scaffolding, It is believed that webpack5 module federation scheme will be the mainstream of micro front-end scheme in the future.
reference
- Webpack official repository
- Module Federation principle research
- Intensive reading Webpack5 new features – module federation
- Webpack 5 Module Federation: A game-changer in JavaScript architecture