I need help migrating the following code from webpack 3 to 4.
new webpack.optimize.CommonsChunkPlugin({
minChunks: module => module.context && module.context.indexOf("node_modules") !== -1,
name: "vendor",
chunks: ["main"]
})
I have two entry files and want only the dependencies of the first one to be included in the vendor chunk. The dependencies of the second entry should all stay in its own bundle.
We have deprecated and removed CommonsChunkPlugin, and have replaced it with a set of defaults and easily overridable API called
optimization.splitChunks
.
webpack.optimize.CommonsChunkPlugin has been removed,
please use config.optimization.splitChunks instead.
You no longer need to use these plugins:
DedupePlugin has been removed too in v4
NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors (on by default in production mode) ModuleConcatenationPlugin -> optimization.concatenateModules (on by default in prod mode) NamedModulesPlugin -> optimization.namedModules (on by default in dev mode)
Use mini-css-extract-plugin
instead of text-extract-plugin
. Use webpack-bundle-analyzer
to analyze your bundled output in graphical way.
Entry scripts are real "Entry-Scripts" to your application, don't add vendor files explicitly to entry:
in webpack.config.js
. SPA apps have one entry and Multi-Page-Apps like classic ASP.NET MVC
apps have multiple entry points. Webpack will build a dependenc graph out of your entry scripts and generate optimized bundles for your app.
If you want to migrate from an older webpack version, it's best to checkout the migration guide
Tree shaking (dead code elimination) is only enabled in production mode.
Webpack 4, the new way of bundling assets
( You have to remove your CommonsChunkPlugin-thinking from your head )
,!! Meanwhile the webpack doc has been updated, a section SplitChunks
was added !!!
Webpack 4 now by default does optimizations automatically. It analyzes your dependency graph and creates optimal bundles (output), based on the following conditions:
- New chunk can be shared OR modules are from the node_modules folder
- New chunk would be bigger than 30kb (before min+gz)
- Maximum number of parallel request when loading chunks on demand <= 5
- Maximum number of parallel request at initial page load <= 3
All this can be tweaked using the SplitChunksPlugin! ( see SplitChunksPlugin documentation )
A more detailed explanation on how to use the new optimization.splitChunks
API.
CommonsChunkPlugin was removed because it has a lot of problems:
The SplitChunksPlugin also has some great properties:
Regarding your issue , you want to split all deps of entry1 and entry2 into separate bundles.
optimization: {
splitChunks: {
cacheGroups: {
"entry1-bundle": {
test: /.../, // <-- use the test property to specify which deps go here
chunks: "all",
name: "entry1-bundle",
/** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
enforce: true,
priority: .. // use the priority, to tell where a shared dep should go
},
"entry2-bundle": {
test: /..../, // <-- use the test property to specify which deps go here
chunks: "all",
name: "entry2-bundle",
enforce: true,
priority: ..
}
}
}
},
If you don't add the optimization:splitChunks entry the default setting is as follows :
splitChunks: {
chunks: 'async',
minSize: 30000,
minRemainingSize: 0,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
You can set optimization.splitChunks.cacheGroups. default to false to disable the default cache group, same for vendors cache group!
Here are some other SplitChunks configuration examples with explanation.
SplitChunksOptions
, CachGroupOptions
and Optimization
can be found here .
The interface definitions below may not be 100% accurate, but good for a simple overview:
SplitChunksOptions
interface
interface SplitChunksOptions { /** Select chunks for determining shared modules (defaults to \"async\", \"initial\" and \"all\" requires adding these chunks to the HTML) */ chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean); /** Minimal size for the created chunk */ minSize?: number; /** Minimum number of times a module has to be duplicated until it's considered for splitting */ minChunks?: number; /** Maximum number of requests which are accepted for on-demand loading */ maxAsyncRequests?: number; /** Maximum number of initial chunks which are accepted for an entry point */ maxInitialRequests?: number; /** Give chunks created a name (chunks with equal name are merged) */ name?: boolean | string | ((...args: any[]) => any); /** Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks) */ cacheGroups?: false | string | ((...args: any[]) => any) | RegExp | { [key: string]: CacheGroupsOptions }; }
CacheGroupsOptions
interface:
interface SplitChunksOptions {
/** Select chunks for determining shared modules (defaults to \"async\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
/** Minimal size for the created chunk */
minSize?: number;
/** Minimum number of times a module has to be duplicated until it's considered for splitting */
minChunks?: number;
/** Maximum number of requests which are accepted for on-demand loading */
maxAsyncRequests?: number;
/** Maximum number of initial chunks which are accepted for an entry point */
maxInitialRequests?: number;
/** Give chunks created a name (chunks with equal name are merged) */
name?: boolean | string | ((...args: any[]) => any);
/** Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks) */
cacheGroups?: false | string | ((...args: any[]) => any) | RegExp | { [key: string]: CacheGroupsOptions };
}
Optimization
Interface
interface CacheGroupsOptions {
/** Assign modules to a cache group */
test?: ((...args: any[]) => boolean) | string | RegExp;
/** Select chunks for determining cache group content (defaults to \"initial\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
/** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
enforce?: boolean;
/** Priority of this cache group */
priority?: number;
/** Minimal size for the created chunk */
minSize?: number;
/** Minimum number of times a module has to be duplicated until it's considered for splitting */
minChunks?: number;
/** Maximum number of requests which are accepted for on-demand loading */
maxAsyncRequests?: number;
/** Maximum number of initial chunks which are accepted for an entry point */
maxInitialRequests?: number;
/** Try to reuse existing chunk (with name) when it has matching modules */
reuseExistingChunk?: boolean;
/** Give chunks created a name (chunks with equal name are merged) */
name?: boolean | string | ((...args: any[]) => any);
}
I have two entry files and want only the dependencies of the first one to be included in the vendor chunk. The dependencies of the second entry should all stay in its own bundle.
Assuming your entrypoints are main
and secondary
:
entry: {
main: 'path-to/main.js',
secondary: 'path-to/secondary.js'
}
Using webpack-4 You can extract only the vendors
modules from main
chunk but leave other third parties modules referenced in secondary
inside that chunk using the test
function of the cacheGroups
you want to create.
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'vendors',
chunks: 'all',
reuseExistingChunk: true,
priority: 1,
enforce: true,
test(module, chunks) {
const name = module.nameForCondition && module.nameForCondition();
return chunks.some(chunk => {
return chunk.name === 'main' && /[\\/]node_modules[\\/]/.test(name);
});
}
},
secondary: {
name: 'secondary',
chunks: 'all',
priority: 2,
enforce: true,
test(module, chunks) {
return chunks.some(chunk => chunk.name === 'secondary');
}
}
}
}
}
This took me a while to figure out, but the key realization for me was that the chunks
argument in webpack 4 now takes a function, which allows you to only include a specific entry. I'm assuming this is a recent change, because at the time of posting it wasn't in the official documentation.
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
chunks: chunk => chunk.name == 'main',
reuseExistingChunk: true,
priority: 1,
test: module =>
/[\\/]node_modules[\\/]/.test(module.context),
minChunks: 1,
minSize: 0,
},
},
},
Please note that I corrected the issue by changing this in my webpack.common.js:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor']
})
]
To this:
optimization: {
runtimeChunk: "single", // enable "runtime" chunk
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all"
}
}
}
},
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.