In the past, one of webpack’s trade-offs when bundling was that each module in your bundle would be wrapped in individual function closures. These wrapper functions made it slower for your JavaScript to execute in the browser. In comparison, tools like Closure Compiler and RollupJS ‘hoist’ or concatenate the scope of all your modules into one closure and allow for your code to have a faster execution time in the browser.
This plugin will enable the same concatenation behavior in webpack. By default this plugin is already enabled inproductionmode
and disabled otherwise. If you need to override the productionmode
optimization, set theoptimization.concatenateModules
option tofalse
. To enable concatenation behavior in other modes, you can addModuleConcatenationPlugin
manually or use theoptimization.concatenateModules
option:
newwebpack.optimize.ModuleConcatenationPlugin();
This concatenation behavior is called “scope hoisting.”
Scope hoisting is specifically a feature made possible by ECMAScript Module syntax. Because of this webpack may fallback to normal bundling based on what kind of modules you are using, andother conditions.
Keep in mind that this plugin will only be applied toES6 modules processed directly by webpack. When using a transpiler, you'll need to disable module processing (e.g. themodules
option in Babel).
As the article explains, webpack attempts to achieve partial scope hoisting. It will merge modules into a single scope but cannot do so in every case. If webpack cannot merge a module, the two alternatives are Prevent and Root. Prevent means the module must be in its own scope. Root means a new module group will be created. The following conditions determine the outcome:
Condition | Outcome |
---|---|
Non ES6 Module | Prevent |
Imported By Non Import | Root |
Imported From Other Chunk | Root |
Imported By Multiple Other Module Groups | Root |
Imported Withimport() | Root |
Affected ByProvidePlugin Or Usingmodule | Prevent |
HMR Accepted | Root |
Usingeval() | Prevent |
In Multiple Chunks | Prevent |
export * from "cjs-module" | Prevent |
The following pseudo JavaScript explains the algorithm:
modules.forEach((module)=>{const group=newModuleGroup({ root: module,}); module.dependencies.forEach((dependency)=>{tryToAdd(group, dependency);});if(group.modules.length>1){ orderedModules=topologicalSort(group.modules); concatenatedModule=newConcatenatedModule(orderedModules); chunk.add(concatenatedModule); orderedModules.forEach((groupModule)=>{ chunk.remove(groupModule);});}});functiontryToAdd(group, module){if(group.has(module)){returntrue;}if(!hasPreconditions(module)){returnfalse;}const nextGroup= group;const result= module.dependents.reduce((check, dependent)=>{return check&&tryToAdd(nextGroup, dependent);},true);if(!result){returnfalse;} module.dependencies.forEach((dependency)=>{tryToAdd(group, dependency);}); group.merge(nextGroup);returntrue;}
When using the webpack CLI, the--stats-optimization-bailout
flag will display bailout reasons. When using the webpack config, add the following to thestats
object:
module.exports={//... stats:{// Display bailout reasons optimizationBailout:true,},};