This repository has been archived by the owner on Aug 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Webpack guide updates for webpack 4 #5798
Merged
addyosmani
merged 13 commits into
google:master
from
iamakulov:webpack-guide-v4-updates
Mar 23, 2018
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
5ec991d
Update the “Decrease front-end size” section for webpack 4
iamakulov 0146508
Fix the paragraph incorrectly merged into note + slightly improve wor…
iamakulov c1cc990
Update the caching section for webpack 4
iamakulov a41cc2b
Rewrite the section on module concatenation + format “in webpack 4” p…
iamakulov 501bf5b
Update the monitoring section for webpack 4
iamakulov 868ee31
Improve formatting
iamakulov cdb3877
Fix the line length warnings where possible
iamakulov 88d33b2
Minificator → minifier
iamakulov be575e6
Merge remote-tracking branch 'upstream/master' into webpack-guide-v4-…
iamakulov 4321552
Replace InlineChunksPlugin with InlineSourcePlugin
iamakulov 26fe7d9
Proof-read the article
iamakulov f893719
Fix the plugin link
iamakulov 9269270
Fix the regular expression
iamakulov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ project_path: /web/fundamentals/_project.yaml | |
book_path: /web/fundamentals/_book.yaml | ||
description: How to use webpack to make your app as small as possible | ||
|
||
{# wf_updated_on: 2018-02-24 #} | ||
{# wf_updated_on: 2018-03-01 #} | ||
{# wf_published_on: 2017-12-18 #} | ||
{# wf_blink_components: N/A #} | ||
|
||
|
@@ -13,11 +13,28 @@ description: How to use webpack to make your app as small as possible | |
One of the first things to do when you’re optimizing an application is to make it as small as | ||
possible. Here’s how to do this with webpack. | ||
|
||
Note: This page covers optimization strategies for webpack 3. A few parts | ||
of it aren’t necessary or work slightly differently with webpack 4. We’re waiting | ||
for the webpack ecosystem to stabilize, and then we’ll update this guide. | ||
## Use the production mode (webpack 4 only) {: #use-the-production-mode } | ||
|
||
## Enable minification | ||
Webpack 4 introduced the new `mode` flag. You could set this flag to `'development'` or | ||
`'production'` to hint webpack that you’re building the application for a specific environment: | ||
|
||
// webpack.config.js | ||
module.exports = { | ||
mode: 'production', | ||
}; | ||
|
||
Make sure to enable the `production` mode when you’re building your app for production. | ||
This will make webpack apply optimizations like minification, removal of development-only code | ||
in libraries, [and more](https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a). | ||
|
||
### Further reading {: .hide-from-toc } | ||
|
||
* [What specific things the `mode` flag configures](https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a) | ||
|
||
## Enable minification {: #enable-minification } | ||
|
||
Note: if you’re using [webpack 4 with the production mode](#enable-the-production-mode), the | ||
bundle-level minification is already enabled. You’ll only need to enable loader-specific options. | ||
|
||
Minification is when you compress the code by removing extra spaces, shortening variable names and | ||
so on. Like this: | ||
|
@@ -39,11 +56,10 @@ so on. Like this: | |
// Minified code | ||
function map(n,r){let t=-1;for(const a=null==n?0:n.length,l=Array(a);++t<a;)l[t]=r(n[t],t,n);return l} | ||
|
||
Webpack supports two ways to minify the code: _the UglifyJS plugin_ and _loader-specific options_. | ||
They should be used simultaneously. | ||
Webpack supports two ways to minify the code: _the bundle-level minification_ and | ||
_loader-specific options_. They should be used simultaneously. | ||
|
||
[The UglifyJS plugin](https://github.com/webpack-contrib/uglifyjs-webpack-plugin) works on the level | ||
of the bundle – it compresses the bundle after compilation. Here’s how it works: | ||
The bundle-level minification compresses the whole bundle after compilation. Here’s how it works: | ||
|
||
<ol> | ||
<li> | ||
|
@@ -82,7 +98,7 @@ function render(data, target) { | |
|
||
<li> | ||
|
||
The UglifyJS plugin minifies it into approximately the following: | ||
A minifier compresses it into approximately the following: | ||
|
||
<pre class="prettyprint"> | ||
// minified bundle.js (part of) | ||
|
@@ -93,7 +109,14 @@ Object.defineProperty(n,"__esModule",{value:!0}),n.render=t;var o=r(1);r.n(o) | |
</li> | ||
</ol> | ||
|
||
The plugin comes bundled with webpack. To enable it, add it to the `plugins` section of the config: | ||
**In webpack 4,** the bundle-level minification is enabled automatically – both in the production | ||
mode and without one. It uses [the UglifyJS minifier](https://github.com/mishoo/UglifyJS2) | ||
under the hood. (If you ever need to disable minification, just use the development mode | ||
or pass `false` to the `optimization.minimize` option.) | ||
|
||
**In webpack 3,** you need to use [the UglifyJS plugin](https://github.com/webpack-contrib/uglifyjs-webpack-plugin) | ||
directly. The plugin comes bundled with webpack; to enable it, add it to the `plugins` | ||
section of the config: | ||
|
||
// webpack.config.js | ||
const webpack = require('webpack'); | ||
|
@@ -104,9 +127,17 @@ The plugin comes bundled with webpack. To enable it, add it to the `plugins` sec | |
], | ||
}; | ||
|
||
Note: In webpack 3, the UglifyJS plugin can’t compile the ES2015+ (ES6+) code. This means | ||
that if your code uses classes, arrow functions or other new language features, | ||
and you don’t compile them into ES5, the plugin will throw an error. <br><br> | ||
If you need to compile the new syntax, use the | ||
[uglifyjs-webpack-plugin](https://github.com/webpack-contrib/uglifyjs-webpack-plugin) package. This | ||
is the same plugin that’s bundled with webpack, but newer, and it’s able to compile the ES2015+ | ||
code. | ||
|
||
The second way is loader-specific options ([what a loader | ||
is](https://webpack.js.org/concepts/loaders/)). With loader options, you can compress things that | ||
the UglifyJS plugin can’t minify. For example, when you import a CSS file with | ||
the minifier can’t minify. For example, when you import a CSS file with | ||
[`css-loader`](https://github.com/webpack-contrib/css-loader), the file is compiled into a string: | ||
|
||
/* comments.css */ | ||
|
@@ -122,7 +153,7 @@ exports=module.exports=__webpack_require__(1)(), | |
exports.push([module.i,<strong>".comment {\r\n color: black;\r\n}"</strong>,""]); | ||
</pre> | ||
|
||
UglifyJS can’t compress this code because it’s a string. To minify the file content, we need to | ||
The minifier can’t compress this code because it’s a string. To minify the file content, we need to | ||
configure the loader to do this: | ||
|
||
<pre class="prettyprint"> | ||
|
@@ -142,13 +173,6 @@ module.exports = { | |
}; | ||
</pre> | ||
|
||
Note: The UglifyJS plugin can’t compile the ES2015+ (ES6+) code. This means that if your code uses | ||
classes, arrow functions or other new language features, and you don’t compile them into ES5, the | ||
plugin will throw an error. <br><br> If you need to compile the new syntax, use the | ||
[uglifyjs-webpack-plugin](https://github.com/webpack-contrib/uglifyjs-webpack-plugin) package. This | ||
is the same plugin that’s bundled with webpack, but newer, and it’s able to compile the ES2015+ | ||
code. | ||
|
||
### Further reading {: .hide-from-toc } | ||
|
||
* [The UglifyJsPlugin docs](https://github.com/webpack-contrib/uglifyjs-webpack-plugin) | ||
|
@@ -159,6 +183,9 @@ code. | |
|
||
## Specify `NODE_ENV=production` | ||
|
||
Note: if you’re using [webpack 4 with the production mode](#enable-the-production-mode), the | ||
`NODE_ENV=production` optimization is already enabled. Feel free to skip this section. | ||
|
||
Another way to decrease the front-end size is to set the `NODE_ENV` | ||
[environmental variable](https://superuser.com/questions/284342/what-are-path-and-other-environment-variables-and-how-can-i-set-or-use-them) | ||
in your code to the value `production`. | ||
|
@@ -194,10 +221,20 @@ React works similarly – it loads a development build that includes the warning | |
// … | ||
|
||
Such checks and warnings are usually unnecessary in production, but they remain in the code and | ||
increase the library size. Configure webpack to remove them with the | ||
[`DefinePlugin`](https://webpack.js.org/plugins/define-plugin/): | ||
increase the library size. **In webpack 4,** remove them by adding | ||
the `optimization.nodeEnv: 'production'` option: | ||
|
||
// webpack.config.js | ||
// webpack.config.js (for webpack 4) | ||
module.exports = { | ||
optimization: { | ||
nodeEnv: 'production', | ||
minimize: true, | ||
}, | ||
}; | ||
|
||
**In webpack 3,** use the [`DefinePlugin`](https://webpack.js.org/plugins/define-plugin/) instead: | ||
|
||
// webpack.config.js (for webpack 3) | ||
const webpack = require('webpack'); | ||
|
||
module.exports = { | ||
|
@@ -209,13 +246,14 @@ increase the library size. Configure webpack to remove them with the | |
], | ||
}; | ||
|
||
The `DefinePlugin` replaces all occurrences of a specified variable with a specific value. With the | ||
Both the `optimization.nodeEnv` option and the `DefinePlugin` work the same way – | ||
they replace all occurrences of `process.env.NODE_ENV` with the specified value. With the | ||
config from above: | ||
|
||
<ol> | ||
<li> | ||
|
||
The <code>DefinePlugin</code> will replace all occurrences of <code>process.env.NODE_ENV</code> with | ||
Webpack will replace all occurrences of <code>process.env.NODE_ENV</code> with | ||
<code>"production"</code>: | ||
|
||
<pre class="prettyprint"> | ||
|
@@ -241,18 +279,11 @@ if (typeof val === 'string') { | |
</pre> | ||
|
||
</li> | ||
</ol> | ||
|
||
Note: If you prefer to configure environment variables via CLI, take a look at the | ||
[EnvironmentPlugin](https://webpack.js.org/plugins/environment-plugin/). It works like the | ||
`DefinePlugin`, but reads the environment and replaces `process.env.` expressions automatically. | ||
|
||
<ol start="2"> | ||
<li> | ||
|
||
And then the <code>UglifyJsPlugin</code> will remove all such <code>if</code> branches – because | ||
<code>"production" !== 'production'</code> is always false, and the plugin understands that the code | ||
inside these branches will never execute: | ||
And then <a href="#enable-minification">the minifier</a> will remove all such | ||
<code>if</code> branches – because <code>"production" !== 'production'</code> is always false, | ||
and the plugin understands that the code inside these branches will never execute: | ||
|
||
<pre class="prettyprint"> | ||
// vue/dist/vue.runtime.esm.js | ||
|
@@ -277,11 +308,6 @@ if (typeof val === 'string') { | |
</li> | ||
</ol> | ||
|
||
Note: You’re not required to use the `UglifyJsPlugin`. You can use any different minifier as soon as | ||
it supports dead code removal (e.g., the [Babel Minify | ||
plugin](https://github.com/webpack-contrib/babel-minify-webpack-plugin) or the [Google Closure | ||
Compiler plugin](https://github.com/roman01la/webpack-closure-compiler)). | ||
|
||
### Further reading {: .hide-from-toc } | ||
|
||
* [What “environment variables” are](https://superuser.com/questions/284342/what-are-path-and-other-environment-variables-and-how-can-i-set-or-use-them) | ||
|
@@ -336,7 +362,7 @@ separate export point in the bundle: | |
|
||
<li> | ||
|
||
The <code>UglifyJsPlugin</code> removes the unused variable: | ||
[The minifier](#enable-minification) removes the unused variable: | ||
|
||
<pre class="prettyprint"> | ||
// bundle.js (part that corresponds to comments.js) | ||
|
@@ -349,10 +375,10 @@ The <code>UglifyJsPlugin</code> removes the unused variable: | |
This works even with libraries if they are written with ES modules. | ||
|
||
Note: In webpack, tree-shaking doesn’t work without a minifier. Webpack just removes export | ||
statements for exports that aren’t used; it’s the `UglifyJsPlugin` that removes unused code. | ||
statements for exports that aren’t used; it’s the minifier that removes unused code. | ||
Therefore, if you compile the bundle without the minifier, it won’t get smaller. <br><br> | ||
You aren’t required to use precisely this plugin though. Any minifier that supports | ||
dead code removal | ||
You aren’t required to use precisely webpack’s built-in minifier (`UglifyJsPlugin`) though. | ||
Any minifier that supports dead code removal | ||
(e.g. [Babel Minify plugin](https://github.com/webpack-contrib/babel-minify-webpack-plugin) | ||
or [Google Closure Compiler plugin](https://github.com/roman01la/webpack-closure-compiler)) | ||
will do the trick. | ||
|
@@ -363,7 +389,8 @@ If you use Babel with `babel-preset-env` or `babel-preset-es2015`, check the set | |
presets. By default, they transpile ES’ `import` and `export` to CommonJS’ `require` and | ||
`module.exports`. [Pass the `{ modules: false }` | ||
option](https://github.com/babel/babel/tree/master/experimental/babel-preset-env) to disable this. | ||
<br><br>The same with TypeScript: remember to set `{ "compilerOptions": { "module": "es2015" } }` in your `tsconfig.json`. | ||
<br><br>The same with TypeScript: remember to set `{ "compilerOptions": { "module": "es2015" } }` | ||
in your `tsconfig.json`. | ||
|
||
### Further reading {: .hide-from-toc } | ||
|
||
|
@@ -495,11 +522,14 @@ files](https://github.com/moment/moment/tree/4caa268356434f3ae9b5041985d62a0e8c2 | |
you don’t use Moment.js with multiple languages, these files will bloat the bundle without a | ||
purpose. | ||
|
||
All these dependencies can be easily optimized. We’ve collected optimization approaches in a GitHub | ||
repo – [check it out](https://github.com/GoogleChromeLabs/webpack-libs-optimizations)! | ||
All these dependencies can be easily optimized. We’ve collected optimization approaches in | ||
a GitHub repo – [check it out](https://github.com/GoogleChromeLabs/webpack-libs-optimizations)! | ||
|
||
## Enable module concatenation for ES modules (aka scope hoisting) | ||
|
||
Note: if you’re using [webpack 4 with the production mode](#enable-the-production-mode), | ||
module concatenation is already enabled. Feel free to skip this section. | ||
|
||
When you are building a bundle, webpack is wrapping each module into a function: | ||
|
||
// index.js | ||
|
@@ -539,8 +569,8 @@ a size and performance overhead for each module. | |
|
||
Webpack 2 introduced support for ES modules which, unlike CommonJS and AMD modules, can be bundled | ||
without wrapping each with a function. And webpack 3 made such bundling possible – with | ||
[`ModuleConcatenationPlugin`](https://webpack.js.org/plugins/module-concatenation-plugin/). Here’s | ||
what this plugin does: | ||
[module concatenation](https://webpack.js.org/plugins/module-concatenation-plugin/). Here’s | ||
what module concatenation does: | ||
|
||
// index.js | ||
import {render} from './comments.js'; | ||
|
@@ -574,12 +604,21 @@ what this plugin does: | |
}) | ||
|
||
See the difference? In the plain bundle, module 0 was requiring `render` from module 1. With | ||
`ModuleConcatenationPlugin`, `require` is simply replaced with required function, and module 1 is | ||
module concatenation, `require` is simply replaced with required function, and module 1 is | ||
removed. The bundle has fewer modules – and less module overhead! | ||
|
||
To enable this behavior, add `ModuleConcatenationPlugin` into the list of plugins: | ||
To turn on this behavior, **in webpack 4**, enable the `optimization.concatenateModules` option: | ||
|
||
// webpack.config.js | ||
// webpack.config.js (for webpack 4) | ||
module.exports = { | ||
optimization: { | ||
concatenateModules: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the default in webpack 4 runnign in prod mode, so this shouldn't be needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, thanks! There’s actually a note about Webpack 4 in the beginning of the section, but it’s worded improperly, so I’ll update it. In the end, the section will have a note like “If you use the production mode, just skip this section”. |
||
}, | ||
}; | ||
|
||
**In webpack 3,** use the `ModuleConcatenationPlugin`: | ||
|
||
// webpack.config.js (for webpack 3) | ||
const webpack = require('webpack'); | ||
|
||
module.exports = { | ||
|
@@ -685,8 +724,9 @@ be excluded from the bundle. This is reasonable – webpack doesn’t know if `i | |
|
||
## Summing up | ||
|
||
* Minimize your code with the `UglifyJsPlugin` and loader options | ||
* Remove the development-only code with the `DefinePlugin` | ||
* Enable the production mode if you use webpack 4 | ||
* Minimize your code with the bundle-level minifier and loader options | ||
* Remove the development-only code by replacing `NODE_ENV` with `production` | ||
* Use ES modules to enable tree shaking | ||
* Compress images | ||
* Apply dependency-specific optimizations | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about this paragraph including a bullet list of the optimizations that are applied through
production
mode, each linked to where you otherwise manually enable them from this guide?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDK, have no opinion here. What’s the benefit/use of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the benefit is making it clear to the reader what the mode does (in case just using
production
without anything else is enough), but we don't need to do that.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s a good point! I would probably link to the webpack docs instead once they are updated – it seems slightly weird for me to link to a section which has a “Skip this section if you’re using the production mode” notice. What do you think?