From a60994f94d12d821b7b1a5981179b8cf87b8b7c7 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Mon, 26 Mar 2018 02:17:19 +0200 Subject: [PATCH 1/8] feat: add documentation for SplitChunksPlugin and deprecation warning on CommonChunks --- src/content/plugins/commons-chunk-plugin.md | 7 +- src/content/plugins/split-chunks-plugin.md | 230 ++++++++++++++++++++ 2 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 src/content/plugins/split-chunks-plugin.md diff --git a/src/content/plugins/commons-chunk-plugin.md b/src/content/plugins/commons-chunk-plugin.md index a21dcc836035..4e42c72278c8 100644 --- a/src/content/plugins/commons-chunk-plugin.md +++ b/src/content/plugins/commons-chunk-plugin.md @@ -6,9 +6,14 @@ contributors: - christopher4lis - kevinzwhuang - jdbevan + - jeremenichelli --- -The `CommonsChunkPlugin` is an opt-in feature that creates a separate file (known as a chunk), consisting of common modules shared between multiple entry points. By separating common modules from bundles, the resulting chunked file can be loaded once initially, and stored in cache for later use. This results in pagespeed optimizations as the browser can quickly serve the shared code from cache, rather than being forced to load a larger bundle whenever a new page is visited. +The `CommonsChunkPlugin` is an opt-in feature that creates a separate file (known as a chunk), consisting of common modules shared between multiple entry points. + +W> The CommonsChunkPlugin has been deprecated in webpack v4 legato. To learn how chunks are treated in the latest version, check out the [SplitChunksPlugin](/plugins/split-chunks-plugin/). + +By separating common modules from bundles, the resulting chunked file can be loaded once initially, and stored in cache for later use. This results in pagespeed optimizations as the browser can quickly serve the shared code from cache, rather than being forced to load a larger bundle whenever a new page is visited. ```javascript new webpack.optimize.CommonsChunkPlugin(options) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md new file mode 100644 index 000000000000..217f7d7b9d7f --- /dev/null +++ b/src/content/plugins/split-chunks-plugin.md @@ -0,0 +1,230 @@ +--- +title: SplitChunksPlugin +contributors: + - sokra + - jeremenichelli +related: + - title: "webpack 4: Code Splitting, chunk graph and the splitChunks optimization" + url: https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 +--- + +Originally in webpack, chunks (and modules imported inside them) were connected by a parent-child relationship in the internal webpack graph. + +This connection didn't allow to further optimizations and resulted into more downloaded code. + +webpack v4 removes the `CommonsChunkPlugin` in favor of `optimization.splitChunks` and `optimize.runtimeChunk` options. Here is how the new flow works. + +## Defaults + +Out of the box `SplitChunksPlugin` should work great for most users. + +By default it only affects on-demand chunks because changing initial chunks would affect the script tags the HTML file should include to run the project. + +webpack will automatically split chunks based on these 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 requests when loading chunks on demand would be lower or equal to 5 +* Maximum number of parallel requests at initial page load would be lower or equal to 3 + +When trying to fulfill the last two conditions, bigger chunks are preferred. + +Let's take a look at some examples. + +### Example 1 + +``` js +// index.js + +// dynamically import a.js +import("./a"); +``` + +``` js +// a.js +import "react"; + +// ... +``` + +**Result:** A separate chunk would be created containing `react`. At the import call this chunk is loaded in parallel to the original chunk containing `./a`. + +Why: + +* Condition 1: The chunk contains modules from `node_modules` +* Condition 2: `react` is bigger than 30kb +* Condition 3: Number of parallel requests at the import call is 2 +* Condition 4: Doesn't affect request at initial page load + +What's the reasoning behind this? `react` probably won't change as often as your application code. By moving it into a separate chunk this chunk can be cached separately from your app code (assuming you are using chunkhash, records, Cache-Control or other long term cache approach). + +### Example 2 + +``` js +// entry.js + +// dynamically import a.js and b.js +import("./a"); +import("./b"); +``` + +``` js +// a.js +import "./helpers"; // helpers is 40kb in size + +// ... +``` + +``` js +// b.js +import "./helpers"; +import "./more-helpers"; // more-helpers is also 40kb in size + +// ... +``` + +**Result:** A separate chunk would be created containing `./helpers` and all dependencies of it. At the import calls this chunk is loaded in parallel to the original chunks. + +Why: + +* Condition 1: The chunk is shared between both import calls +* Condition 2: `helpers` is bigger than 30kb +* Condition 3: Number of parallel requests at the import calls is 2 +* Condition 4: Doesn't affect request at initial page load + +Putting the content of `helpers` into each chunk will result into its code being downloaded twice. By using a separate chunk this will only happen once. We pay the cost of an additional request, which could be considered a tradeoff. That's why there is a minimum size of 30kb. + +## Configuration + +For developers that want to have more control over this functionality, webpack provides a set of options to better fit your needs. + +If you are manually changing the split configuration, measure the impact of the changes to see and make sure there's a real benefit. + +W> Default configuration was chosen to fit web performance best practices but the optimum strategy for your project might defer depending on the nature of it. + +### Configuring cache groups + +The defaults assign all modules from `node_modules` to a cache group called `vendors` and all modules duplicated in at least 2 chunks to a cache group `default`. + +A module can be assigned to multiple cache groups. The optimization then prefers the cache group with the higher `priority` (`priority` option) or that one that forms bigger chunks. + +### Conditions + +Modules from the same chunks and cache group will form a new chunk when all conditions are fulfilled. + +There are 4 options to configure the conditions: + +* `minSize` (default: 30000) Minimum size for a chunk. +* `minChunks` (default: 1) Minimum number of chunks that share a module before splitting +* `maxInitialRequests` (default 3) Maximum number of parallel requests at an entrypoint +* `maxAsyncRequests` (default 5) Maximum number of parallel requests at on-demand loading + +### Naming + +To control the chunk name of the split chunk the `name` option can be used. + +W> When assigning equal names to different split chunks they are merged together. This can be used to put all vendor modules into a single shared chunk, but it's not recommend since it can result in more code downloaded. + +The magic value `true` automatically chooses a name based on chunks and cache group key, otherwise a string or function can be passed. + +When the name matches an entrypoint name, the entrypoint is removed. + +### Select modules + +The `test` option controls which modules are selected by this cache group. Omitting it selects all modules. It can be a RegExp, string or function. + +It can match the absolute module resource path or chunk names. When a chunk name is matched, all modules in this chunk are selected. + +### Select chunks + +With the `chunks` option the selected chunks can be configured. + +There are 3 values possible `"initial"`, `"async"` and `"all"`. When configured the optimization only selects initial chunks, on-demand chunks or all chunks. + +The option `reuseExistingChunk` allows to reuse existing chunks instead of creating a new one when modules match exactly. + +This can be controlled per cache group. + +### `optimization.splitChunks.chunks: all` + +As it was mentioned before this plugin will affect dynamic imported modules. Setting the `optimization.splitChunks.chunks` option to `"all"` initial chunks will get affected by it (even the ones not imported dynamically). This way chunks can even be shared between entrypoints and on-demand loading. + +This is the recommended configuration. + +T> You can combine this configuration with the [HtmlWebpackPlugin](/plugins/html-webpack-plugin/), it will inject all the generated vendor chunks for you. + +## `optimization.splitChunks` + +This configuration object represents the default behavior of the `SplitChunksPlugin`. + +```js +splitChunks: { + chunks: "async", + minSize: 30000, + minChunks: 1, + maxAsyncRequests: 5, + maxInitialRequests: 3, + name: true, + cacheGroups: { + default: { + minChunks: 2, + priority: -20, + reuseExistingChunk: true + }, + vendors: { + test: /[\\/]node_modules[\\/]/, + priority: -10 + } + } +} +``` + +By default cache groups inherit options from `splitChunks.*`, but `test`, `priority` and `reuseExistingChunk` can only be configured on cache group level. + +`cacheGroups` is an object where keys are the cache group names. All options from the ones listed above are possible: `chunks`, `minSize`, `minChunks`, `maxAsyncRequests`, `maxInitialRequests`, `name`. + +You can set `optimization.splitChunks.cacheGroups.default` to `false` to disable the default cache group, same for `vendors` cache group. + +The priority of the default groups are negative to allow any custom cache group to take higher priority (the default value is `0`). + +Here are some examples and their effect: + +### Example 1 + +Create a `commons` chunk, which includes all code shared between entrypoints. + +```js +splitChunks: { + cacheGroups: { + commons: { + name: "commons", + chunks: "initial", + minChunks: 2 + } + } +} +``` + +W> This downloads more code than neccessary. + +### Example 2 + +Create a `vendors` chunk, which includes all code from node_modules in the whole application. + +``` js +splitChunks: { + cacheGroups: { + commons: { + test: /[\\/]node_modules[\\/], + name: "vendors", + chunks: "all" + } + } +} +``` + +W> This downloads more code than neccessary. + +## `optimization.runtimeChunk` + +Setting `optimization.runtimeChunk` to `true` adds an additonal chunk to each entrypoint containing only the runtime. From 80509e452b9e0779de01f6e22f95328ac4233918 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Tue, 27 Mar 2018 01:49:30 +0200 Subject: [PATCH 2/8] fix: repeated headers --- src/content/plugins/split-chunks-plugin.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index 217f7d7b9d7f..55df12c4aee8 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -31,7 +31,7 @@ When trying to fulfill the last two conditions, bigger chunks are preferred. Let's take a look at some examples. -### Example 1 +### Defaults: Example 1 ``` js // index.js @@ -58,7 +58,7 @@ Why: What's the reasoning behind this? `react` probably won't change as often as your application code. By moving it into a separate chunk this chunk can be cached separately from your app code (assuming you are using chunkhash, records, Cache-Control or other long term cache approach). -### Example 2 +### Defaults: Example 2 ``` js // entry.js @@ -189,7 +189,7 @@ The priority of the default groups are negative to allow any custom cache group Here are some examples and their effect: -### Example 1 +### Split Chunks: Example 1 Create a `commons` chunk, which includes all code shared between entrypoints. @@ -207,7 +207,7 @@ splitChunks: { W> This downloads more code than neccessary. -### Example 2 +### Split Chunks: Example 2 Create a `vendors` chunk, which includes all code from node_modules in the whole application. From 9e91154d6e2e607312b2862fa4ef952ca0fe27f7 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Fri, 30 Mar 2018 02:18:56 +0200 Subject: [PATCH 3/8] fix: more accurate warnings and typo fixes --- src/content/plugins/commons-chunk-plugin.md | 2 +- src/content/plugins/split-chunks-plugin.md | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/content/plugins/commons-chunk-plugin.md b/src/content/plugins/commons-chunk-plugin.md index 4e42c72278c8..398e901d7bf2 100644 --- a/src/content/plugins/commons-chunk-plugin.md +++ b/src/content/plugins/commons-chunk-plugin.md @@ -11,7 +11,7 @@ contributors: The `CommonsChunkPlugin` is an opt-in feature that creates a separate file (known as a chunk), consisting of common modules shared between multiple entry points. -W> The CommonsChunkPlugin has been deprecated in webpack v4 legato. To learn how chunks are treated in the latest version, check out the [SplitChunksPlugin](/plugins/split-chunks-plugin/). +W> The CommonsChunkPlugin has been removed in webpack v4 legato. To learn how chunks are treated in the latest version, check out the [SplitChunksPlugin](/plugins/split-chunks-plugin/). By separating common modules from bundles, the resulting chunked file can be loaded once initially, and stored in cache for later use. This results in pagespeed optimizations as the browser can quickly serve the shared code from cache, rather than being forced to load a larger bundle whenever a new page is visited. diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index 55df12c4aee8..482ee3b5209e 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -147,7 +147,7 @@ This can be controlled per cache group. ### `optimization.splitChunks.chunks: all` -As it was mentioned before this plugin will affect dynamic imported modules. Setting the `optimization.splitChunks.chunks` option to `"all"` initial chunks will get affected by it (even the ones not imported dynamically). This way chunks can even be shared between entrypoints and on-demand loading. +As it was mentioned before this plugin will affect dynamic imported modules. Setting the `optimization.splitChunks.chunks` option to `"all"` initial chunks will get affected by it (even the ones not imported dynamically). This way chunks can even be shared between entry points and on-demand loading. This is the recommended configuration. @@ -191,7 +191,7 @@ Here are some examples and their effect: ### Split Chunks: Example 1 -Create a `commons` chunk, which includes all code shared between entrypoints. +Create a `commons` chunk, which includes all code shared between entry points. ```js splitChunks: { @@ -205,17 +205,17 @@ splitChunks: { } ``` -W> This downloads more code than neccessary. +W> This configuration can enlarge your initial bundles, it is recommended to use dynamic imports when a module is not immediately needed. ### Split Chunks: Example 2 -Create a `vendors` chunk, which includes all code from node_modules in the whole application. +Create a `vendors` chunk, which includes all code from `node_modules` in the whole application. ``` js splitChunks: { cacheGroups: { commons: { - test: /[\\/]node_modules[\\/], + test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all" } @@ -223,8 +223,10 @@ splitChunks: { } ``` -W> This downloads more code than neccessary. +W> This might result in a large chunk containing all external packages. It is recommended to only include your core frameworks and utilities and dynamically load the rest of the dependencies. + +## `optimize.runtimeChunk` -## `optimization.runtimeChunk` +Setting `optimize.runtimeChunk` to `true` adds an additonal chunk to each entrypoint containing only the runtime. -Setting `optimization.runtimeChunk` to `true` adds an additonal chunk to each entrypoint containing only the runtime. +The value `single` instead creates a runtime file to be shared for all generated chunks. From 9907ea8a925aa97a53acf778eb03631eb6dd3157 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Fri, 30 Mar 2018 16:32:05 +0200 Subject: [PATCH 4/8] feat: add automaticNameDelimiter information --- src/content/plugins/split-chunks-plugin.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index 482ee3b5209e..ad1d40968da6 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -123,11 +123,19 @@ There are 4 options to configure the conditions: To control the chunk name of the split chunk the `name` option can be used. -W> When assigning equal names to different split chunks they are merged together. This can be used to put all vendor modules into a single shared chunk, but it's not recommend since it can result in more code downloaded. +W> When assigning equal names to different split chunks, all vendor modules are placed into a single shared chunk, though it's not recommend since it can result in more code downloaded. The magic value `true` automatically chooses a name based on chunks and cache group key, otherwise a string or function can be passed. -When the name matches an entrypoint name, the entrypoint is removed. +When the name matches an entry point name, the entry point is removed. + +#### `optimization.splitChunks.automaticNameDelimiter` + +By default webpack will generate names using origin and name of the chunk, like `vendors~main.js`. + +If your project has a conflict with the `~` character, it can be changed by setting this option to any other value that works for your project: `automaticNameDelimiter: "-"`. + +Then the resulting names will look like `vendors-main.js`. ### Select modules @@ -164,6 +172,7 @@ splitChunks: { minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, + automaticNameDelimiter: '~', name: true, cacheGroups: { default: { From 77742bc4de400884be872e7e9150882fe2d88ca5 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Sun, 1 Apr 2018 13:53:46 +0200 Subject: [PATCH 5/8] fix: indentation on code sample --- src/content/plugins/split-chunks-plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index ad1d40968da6..c599875a594c 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -172,7 +172,7 @@ splitChunks: { minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, - automaticNameDelimiter: '~', + automaticNameDelimiter: '~', name: true, cacheGroups: { default: { From 6c02a9257a507b72ffbbd6f1c75e652aefbd4099 Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Sun, 1 Apr 2018 14:23:49 +0200 Subject: [PATCH 6/8] fix: alexignore all generated plugins readmes --- .alexignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.alexignore b/.alexignore index 94c96d1f7837..90471bcb2796 100644 --- a/.alexignore +++ b/.alexignore @@ -1 +1 @@ -generated/plugins/npm-install-webpack-plugin.md +generated/plugins/* From a6355decbf0d61dddfa779eab3e145e84334611d Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Sun, 1 Apr 2018 14:49:27 +0200 Subject: [PATCH 7/8] fix: add extra linebreak before h2 --- src/content/plugins/split-chunks-plugin.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index c599875a594c..9ac8f63f465c 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -14,6 +14,7 @@ This connection didn't allow to further optimizations and resulted into more dow webpack v4 removes the `CommonsChunkPlugin` in favor of `optimization.splitChunks` and `optimize.runtimeChunk` options. Here is how the new flow works. + ## Defaults Out of the box `SplitChunksPlugin` should work great for most users. @@ -94,6 +95,7 @@ Why: Putting the content of `helpers` into each chunk will result into its code being downloaded twice. By using a separate chunk this will only happen once. We pay the cost of an additional request, which could be considered a tradeoff. That's why there is a minimum size of 30kb. + ## Configuration For developers that want to have more control over this functionality, webpack provides a set of options to better fit your needs. @@ -153,6 +155,7 @@ The option `reuseExistingChunk` allows to reuse existing chunks instead of creat This can be controlled per cache group. + ### `optimization.splitChunks.chunks: all` As it was mentioned before this plugin will affect dynamic imported modules. Setting the `optimization.splitChunks.chunks` option to `"all"` initial chunks will get affected by it (even the ones not imported dynamically). This way chunks can even be shared between entry points and on-demand loading. @@ -161,6 +164,7 @@ This is the recommended configuration. T> You can combine this configuration with the [HtmlWebpackPlugin](/plugins/html-webpack-plugin/), it will inject all the generated vendor chunks for you. + ## `optimization.splitChunks` This configuration object represents the default behavior of the `SplitChunksPlugin`. @@ -234,6 +238,7 @@ splitChunks: { W> This might result in a large chunk containing all external packages. It is recommended to only include your core frameworks and utilities and dynamically load the rest of the dependencies. + ## `optimize.runtimeChunk` Setting `optimize.runtimeChunk` to `true` adds an additonal chunk to each entrypoint containing only the runtime. From c1350e320a1eed8ffb53134829185fb410c7483e Mon Sep 17 00:00:00 2001 From: Jere Menichelli Date: Mon, 2 Apr 2018 13:54:34 +0200 Subject: [PATCH 8/8] chore: rephrase introduction on split chunks --- src/content/plugins/split-chunks-plugin.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/content/plugins/split-chunks-plugin.md b/src/content/plugins/split-chunks-plugin.md index 9ac8f63f465c..bb9b7904420b 100644 --- a/src/content/plugins/split-chunks-plugin.md +++ b/src/content/plugins/split-chunks-plugin.md @@ -8,11 +8,9 @@ related: url: https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 --- -Originally in webpack, chunks (and modules imported inside them) were connected by a parent-child relationship in the internal webpack graph. +Originally, chunks (and modules imported inside them) were connected by a parent-child relationship in the internal webpack graph. The `CommonsChunkPlugin` was used to avoid duplicated dependencies across them, but further optimizations where not possible -This connection didn't allow to further optimizations and resulted into more downloaded code. - -webpack v4 removes the `CommonsChunkPlugin` in favor of `optimization.splitChunks` and `optimize.runtimeChunk` options. Here is how the new flow works. +Since version 4 the `CommonsChunkPlugin` was removed in favor of `optimization.splitChunks` and `optimize.runtimeChunk` options. Here is how the new flow works. ## Defaults