{"id":139,"date":"2017-05-09T23:15:21","date_gmt":"2017-05-09T20:15:21","guid":{"rendered":"https:\/\/iamakulov.com\/notes2\/?p=139"},"modified":"2018-01-21T11:51:47","modified_gmt":"2018-01-21T08:51:47","slug":"webpack-front-end-size-caching","status":"publish","type":"post","link":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/","title":{"rendered":"webpack for real tasks: decreasing front-end size and improving caching"},"content":{"rendered":"<div class=\"webpack-navigator-2\">\nThis is the second part of a three-part introduction into webpack:<\/p>\n<ol>\n<li><a href=\"https:\/\/iamakulov.com\/notes\/webpack-for-real-tasks-part-1\">Bundling front-end and adding compilation<\/a>\n<li>Decreasing front-end size and improving caching <em>(you are here!)<\/em><\/li>\n<li>Speeding up build and improving the development workflow<\/li>\n<\/ol>\n<p>Want to stay tuned for the future posts? <a href=\"#subscribe\">Subscribe<\/a><\/p>\n<\/div>\n<p><em>Last updated on 21 Jan 2018: replaced the recommended plugin in <a href=\"#moment-js\">the Moment.js part<\/a>.<\/em><\/p>\n<h1 id=\"task-decrease-front-end-size\"><span class=\"task-badge\">Task<\/span> Decrease front-end size<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#task-decrease-front-end-size\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p><em>Given:<\/em> you have a front-end application. You want to decrease its size to make it load faster.<\/p>\n<p>Let\u2019s see how webpack can help with this.<\/p>\n<h2 id=\"minification\">Minification<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#minification\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>Minification is when you compress your code by removing extra spaces, shortening variable names, etc. Like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png\" alt=\"\" width=\"1900\" height=\"450\" class=\"alignnone size-full wp-image-205\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png 1900w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2-300x71.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2-768x182.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2-1024x243.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2-1200x284.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>Webpack has two approaches to minify the code: <em>the UglifyJS plugin<\/em> and <em>loaders options<\/em>. They should be used simultaneously.<\/p>\n<p>The UglifyJS plugin works on the level of the bundle and compresses it after compilation. As you might\u2019ve guessed, it used <a href=\"https:\/\/github.com\/mishoo\/UglifyJS2\">UglifyJS<\/a> under the hood. This is how it works: <\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You write code like this<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ comments.js\r\nimport '.\/comments.css';\r\nexport function render(data, target) {\r\n    console.log('Rendered!');\r\n}\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>\u2193<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>Webpack compiles it into approximately the following<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ bundle.js (part of)\r\n&quot;use strict&quot;;\r\nObject.defineProperty(__webpack_exports__, &quot;__esModule&quot;, { value: true });\r\nvar __WEBPACK_IMPORTED_MODULE_0__comments_css__ =\r\n  __webpack_require__(4);\r\nvar __WEBPACK_IMPORTED_MODULE_0__comments_css___default =\r\n  __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__comments_css__);\r\n__webpack_exports__[&quot;render&quot;] = render;\r\n\r\nfunction render(data, target) {\r\n    console.log('Rendered!');\r\n}\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>\u2193<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>The UglifyJS plugin minifies it into approximately the following<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ bundle.js (part of)\r\n&quot;use strict&quot;;function r(e,t){console.log(&quot;Rendered!&quot;)}\r\nObject.defineProperty(t,&quot;__esModule&quot;,{value:!0});\r\nvar o=n(4);n.n(o);t.render=r\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p style=\"margin-top: 1.75em\">To enable the plugin, add it to the <code>plugins<\/code> section of the config:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nconst webpack = require('webpack');\r\n\r\nmodule.exports = {\r\n  plugins: [\r\n    new webpack.optimize.UglifyJsPlugin()\r\n  ]\r\n};\r\n<\/code><\/pre>\n<p>The second approach is loaders options. It allows compressing things that UglifyJS can\u2019t minify. Its point is that some code (e.g. CSS that you import) is compiled as a string which UglifyJS can\u2019t handle:<\/p>\n<pre><code >\/* comments.css *\/\r\n.comment {\r\n    color: black;\r\n}\r\n<\/code><\/pre>\n<p>\u2193<\/p>\n<pre><code >\/\/ bundle.js (part of)\r\nexports = module.exports = __webpack_require__(1)();\r\nexports.push([module.i, &quot;.comment {\\r\\n    color: black;\\r\\n}&quot;, &quot;&quot;]);\r\n<\/code><\/pre>\n<p>To minify it, you should configure the loader. Here\u2019s how you do it with <a href=\"https:\/\/github.com\/webpack-contrib\/css-loader\"><em>css-loader<\/em><\/a>:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nmodule.exports = {\r\n  module: {\r\n    rules: [\r\n      {\r\n        test: \/\\.css$\/,\r\n        use: [\r\n          'style-loader',\r\n          { loader: 'css-loader', options: { minimize: true } }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n};\r\n<\/code><\/pre>\n<div class=\"snippet\">\n<h2 id=\"pitfall-es2015-code\">Pitfall: ES2015 code<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-es2015-code\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>UglifyJS 2 (which is used in webpack) can\u2019t compile ES2015+ code. This means that if your code uses classes, arrow functions or other new language features, and you don\u2019t compile it to ES5, UglifyJS won\u2019t handle it. In this case, you can use Babili, a Babel-based minifier. See <a href=\"https:\/\/www.npmjs.com\/package\/babili-webpack-plugin\">babili-webpack-plugin<\/a>\n<\/div>\n<h2 id=\"node_envproduction\">NODE_ENV=production<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#node_envproduction\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>Another way to decrease the front-end size is to set <code>NODE_ENV<\/code> environmental variable to the value \u201cproduction\u201d.<\/p>\n<p><code>NODE_ENV<\/code> is an environmental variable that is commonly used in libraries to detect in which mode the library works \u2013 in development mode or on a production server. The library can behave differently based on this variable. For example, React does additional checks and prints warnings when it\u2019s built for development:<\/p>\n<pre><code >\/\/ \u2026\r\n\r\nif (process.env.NODE_ENV !== 'production') {\r\n  validateTypeDef(Constructor, propTypes, 'prop');\r\n}\r\n\r\n\/\/ \u2026\r\n<\/code><\/pre>\n<p>When you\u2019re building your app for production, it\u2019s better to also tell that your libraries. For Node.js libraries, it\u2019s done by configuring the environment and setting the <code>NODE_ENV<\/code> variable to \u201cproduction\u201d. For front-end libraries, it\u2019s done by replacing <code>process.env.NODE_ENV<\/code> with a specific value:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nconst webpack = require('webpack');\r\n\r\nmodule.exports = {\r\n  plugins: {\r\n    new webpack.DefinePlugin({\r\n      'process.env.NODE_ENV': '&quot;production&quot;'\r\n    })\r\n  }\r\n};\r\n<\/code><\/pre>\n<p><a href=\"https:\/\/webpack.js.org\/plugins\/define-plugin\/\">DefinePlugin<\/a> takes an object with keys referring to variables to be replaced and values referring to the values that should be substituted. With this configuration, it\u2019ll replace all <code>process.env.NODE_ENV<\/code> instances with <code>\"production\"<\/code>, which will make UglifyJS understand that the comparison expression is always false and remove it:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-40-15-1.png\" alt=\"\" width=\"1700\" height=\"950\" class=\"alignnone size-full wp-image-180\" style=\"border: 1px solid #ccc\" \/><\/p>\n<h2 id=\"ecmascript-imports\">ECMAScript imports<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#ecmascript-imports\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>The next way to decrease the front-end size is to use ECMAScript imports and exports.<\/p>\n<p>When you use ECMAScript imports and exports, webpack becomes able to do tree-shaking. Tree-shaking is when a bundler traverses your whole dependency tree, checks what of them are used, and keeps only the used ones. So, if you use ECMAScript module syntax, webpack can eliminate the unused code:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You write two files where only one export is used<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ comments.js\r\nexport const commentRestEndpoint = '\/rest\/comments';\r\nexport const render = () =&gt; { return 'Rendered!'; };\r\n\r\n\/\/ index.js\r\nimport { render } from '.\/comments.js';\r\nrender();\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>\u2193<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>Webpack realizes that <code>commentRestEndpoint<\/code> is not used and doesn\u2019t generate a separate export point in the bundle<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ bundle.js (part of)\r\n(function(module, __webpack_exports__, __webpack_require__) {\r\n  &quot;use strict&quot;;\r\n  \/* unused harmony export commentRestEndpoint *\/\r\n  \/* harmony export *\/__webpack_exports__[&quot;b&quot;] = render;\r\n\r\n  var commentRestEndpoint = '\/rest\/comments';\r\n  var render = function () { return 'Rendered!'; }\r\n})\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>\u2193<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>UglifyJS removes the unused variable<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ bundle.js (part of)\r\n(function(n,e){&quot;use strict&quot;;e.b=r;var r=function(){return&quot;Rendered!&quot;}})\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>This works even with libraries; the library should also be written with ECMAScript modules.<\/p>\n<div class=\"snippet\">\n<h2 id=\"pitfall-tree-shaking-doesnt-work-without-uglifyjs\">Pitfall: tree-shaking doesn\u2019t work without UglifyJS<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-tree-shaking-doesnt-work-without-uglifyjs\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>The less-known fact is that the unused code is removed not by webpack, but by UglifyJS. Webpack just removes export statements for the exports that aren\u2019t used, which makes them possible to be removed by a minifier. Therefore, if you compile your bundle without the minifier, the bundle won\u2019t get smaller.<\/p>\n<p>See how to enable UglifyJS <a href=\"#minification\">in the \u201cMinification\u201d section<\/a>.<\/p>\n<h2 id=\"pitfall-dont-transpile-ecmascript-imports-to-the-commonjs-format\">Pitfall: don\u2019t transpile ECMAScript imports to the CommonJS format<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-dont-transpile-ecmascript-imports-to-the-commonjs-format\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>If you use Babel with <a href=\"https:\/\/github.com\/babel\/babel-preset-env\">babel-preset-env<\/a> or <a href=\"https:\/\/github.com\/babel\/babel-preset-es2015\">babel-preset-es2015<\/a>, check the settings of these presets. By default, they transpile ECMAScript\u2019s <code>import<\/code> and <code>export<\/code> to CommonJS\u2019 <code>require<\/code> and <code>module.exports<\/code>. Pass the <code>{ modules: false }<\/code> option to disable this:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nmodule.exports = {\r\n  module: {\r\n    rules: [\r\n      {\r\n        test: \/\\.js$\/,\r\n        use: [{'babel-loader', options: {\r\n          presets: [['es2015', { modules: false }]]\r\n        }]\r\n      }\r\n    ]\r\n  }\r\n};\r\n<\/code><\/pre>\n<h2 id=\"pitfall-complex-cases-arent-optimized\">Pitfall: complex cases aren\u2019t optimized<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-complex-cases-arent-optimized\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>In some complex cases \u2013 e. g. when you re-export something (<code>export * from 'file.js'<\/code>), or when you compile classes with the TypeScript compiler \u2013 webpack can\u2019t optimize your bundle. The bad things about this are that the cases when this happens aren\u2019t obvious, and it\u2019s unclear when this will be fixed. Here\u2019s the corresponding GitHub issue: <a href=\"https:\/\/github.com\/webpack\/webpack\/issues\/2867#issuecomment-247288543\">webpack\/webpack#2867<\/a><\/p>\n<p>Webpack team <a href=\"https:\/\/twitter.com\/wSokra\/status\/893028949495869440\">is working on a solution for re-exports though<\/a>.<\/p>\n<\/div>\n<h2 id=\"moment-js\">Moment.js<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#moment-js\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>Tested with moment.js 2.18.1<\/div>\n<div class='annotated-element__content'>\n<p><a href=\"https:\/\/github.com\/moment\/moment\">Moment.js<\/a> is a library for working with dates. By default, when you include it in your app, it takes 217 kB of minified code. That\u2019s huge \u2013 the average size of JavaScript on a page <a href=\"http:\/\/httparchive.org\/interesting.php?a=All&amp;l=Apr%201%202017\">was 417 kB in April 2017<\/a>. The good part, however, is that it can be easily reduced.<\/p>\n<\/div>\n<\/div>\n<p>165 kB of the size of moment.js is localization files. They\u2019re included even if you don\u2019t use them. This happens because moment.js chooses the localization files dynamically, during runtime:<\/p>\n<p><a href=\"https:\/\/github.com\/moment\/moment\/blob\/8cd97f2d7af848113987339ff228b5b9b29af908\/src\/lib\/locale\/locales.js#L55\" class=\"image-link\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55.png\" alt=\"\" width=\"1444\" height=\"693\" class=\"alignnone size-full wp-image-183\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55.png 1444w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55-300x144.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55-768x369.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55-1024x491.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_19-54-55-1200x576.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><\/p>\n<p>Webpack doesn\u2019t know which files you\u2019ll need, so it includes <em>all<\/em> files from the <em>locale<\/em> directory.<\/p>\n<p>To deal with it, specify the exact files with <em><a href=\"https:\/\/github.com\/iamakulov\/moment-locales-webpack-plugin\">MomentLocalesPlugin<\/a><\/em>:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nconst MomentLocalesPlugin = require('moment-locales-webpack-plugin');\r\n\r\nmodule.exports = {\r\n  plugins: [\r\n    \/\/ Or: To strip all locales except \u201cen\u201d, \u201ces-us\u201d and \u201cru\u201d\r\n    \/\/ (\u201cen\u201d is built into Moment and can\u2019t be removed)\r\n    new MomentLocalesPlugin({\r\n      localesToKeep: ['es-us', 'ru'],\r\n    }),\r\n  ],\r\n};\r\n<\/code><\/pre>\n<h2 id=\"lodash\">Lodash<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#lodash\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p><a href=\"https:\/\/github.com\/lodash\/lodash\">Lodash<\/a> is a collection of JavaScript utilities.<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>Tested with Lodash 4.17.4<\/div>\n<div class='annotated-element__content'>\n<p>When you include Lodash, your bundle grows by 72 KB of minified code. That\u2019s the size of all the 316 Lodash methods. If you use only, like, 20 of them, then approximately 65 KB of the code do just nothing except slowing down the page loading.<\/p>\n<\/div>\n<\/div>\n<p>Thankfully, Lodash lets you include only the methods you need. The most basic way to do this is to import methods from the files they\u2019re implemented in:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>72 KB \u2192 8.27 KB<\/div>\n<div class='annotated-element__content'>\n<pre><code >- import _ from 'lodash';\r\n- _.get();\r\n+ import get from 'lodash\/get';\r\n+ get();\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>This approach might work if you\u2019re starting a project from scratch, but it doesn\u2019t work for existing projects. What you\u2019re gonna do, rewrite all imports? That\u2019s too much work. That\u2019s why I prefer using <code>babel-plugin-lodash<\/code> and sometimes <code>lodash-webpack-plugin<\/code>.<\/p>\n<p><a href=\"https:\/\/github.com\/lodash\/babel-plugin-lodash\"><code>babel-plugin-lodash<\/code><\/a> is a plugin for Babel that replaces generic imports with concrete ones during compilation. That is, it does exactly the same thing as depicted in the snippet above:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>72 KB \u2192 8.27 KB<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ Before babel-plugin-lodash\r\nimport _ from 'lodash';\r\n_.get({ a: { b: 5 } }, 'a.b');\r\n\r\n\u2193\r\n\r\n\/\/ After babel-plugin-lodash\r\nimport _get from 'lodash\/get';\r\n_get({ a: { b: 5 } }, 'a.b');\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p><a href=\"https:\/\/github.com\/lodash\/lodash-webpack-plugin\"><code>lodash-webpack-plugin<\/code><\/a> is a plugin for webpack that modifies Lodash behavior by removing some code and thus cutting the bundle size. For example, <code>_.get<\/code> by default supports deep paths. If you don\u2019t need this, you can enable <code>lodash-webpack-plugin<\/code>, which will remove this support:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>72 KB \u2192 772 B<\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ Before babel-plugin-lodash + lodash-webpack-plugin\r\nimport _ from 'lodash';\r\n_.get({ a: { b: 5 } }, 'a.b');\r\n\/\/ \u2192\u00a0returns 5\r\n\r\n\u2193\r\n\r\n\/\/ After babel-plugin-lodash + lodash-webpack-plugin\r\nimport _get from 'lodash\/get';\r\n_get({ a: { b: 5 } }, 'a.b');\r\n\/\/ \u2192\u00a0returns undefined\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>Keep in mind, however, that you can\u2019t just enable the plugin and leave it as-is. This plugin changes the Lodash functionality, so your existing code could break. <a href=\"https:\/\/github.com\/lodash\/lodash-webpack-plugin#feature-sets\">Take a look at the list of features<\/a> it removes by default.<\/p>\n<div class=\"snippet\">\n<h2 id=\"pitfall-duplicated-lodash\">Pitfall: duplicated Lodash<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-duplicated-lodash\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>There\u2019re two common versions of Lodash: the <a href=\"https:\/\/www.npmjs.com\/package\/lodash\"><code>lodash<\/code><\/a> package and the <a href=\"https:\/\/www.npmjs.com\/package\/lodash-es\"><code>lodash-es<\/code><\/a> package (which is Lodash with ES exports). If you use the former package, and one of your dependencies uses the latter, you will find yourself having two Lodashes in a single bundle. To avoid this, <a href=\"https:\/\/webpack.js.org\/configuration\/resolve\/#resolve-alias\">alias <code>lodash<\/code> to <code>lodash-es<\/code><\/a> (or vice versa).<\/p>\n<p>An example of a package that uses <code>lodash-es<\/code> is Redux.<\/p>\n<p>Thanks to <a href=\"https:\/\/twitter.com\/7rulnik\">Valentin Semirulnik<\/a> for this tip.\n<\/div>\n<h2 id=\"externals\">externals<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#externals\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>Sometimes you have a large project where some code is compiled with webpack and some code is not. Like a page with crosswords, where the crosswords module is built with webpack, and the site around it is not:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08.png\" alt=\"\" width=\"1920\" height=\"1080\" class=\"alignnone size-full wp-image-185\" style=\"border: 1px solid #ccc\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08.png 1920w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08-300x169.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08-768x432.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08-1024x576.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-05-08-1200x675.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>If both pieces of code have common dependencies, you can share the dependencies between them. This is done with the webpack\u2019s <code>externals<\/code> option which lets you alias module imports to something different.<\/p>\n<p>The common usage is when you have an instance of a library in the global object (like <code>window<\/code>), and you want to alias the library imports to this instance. In this case, you pass an object mapping the module names to the variable names:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nmodule.exports = {\r\n  externals: {\r\n    'react': 'React',\r\n    'react-dom': 'ReactDOM',\r\n  }\r\n};\r\n<\/code><\/pre>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'><a href='https:\/\/github.com\/webpack\/webpack\/tree\/master\/examples\/externals'>Example of the bundle generated with <code>externals<\/code><\/a><\/div>\n<div class='annotated-element__content'>\n<p>Webpack will replace all module references with variable references.<\/p>\n<\/div>\n<\/div>\n<p>A less known approach is when the old code doesn\u2019t put the libraries into the global object but loads them with an AMD-compatible loader. In this case, you can compile your webpack as an AMD bundle and alias modules to paths to the libraries:<\/p>\n<pre><code >\/\/ webpack.config.js\r\nmodule.exports = {\r\n  output: { libraryTarget: 'amd' },\r\n\r\n  externals: {\r\n    'react': { amd: '\/libraries\/react.min.js' },\r\n    'react-dom': { amd: '\/libraries\/react-dom.min.js' },\r\n  }\r\n};\r\n<\/code><\/pre>\n<p>Webpack will wrap your bundle into <code>define()<\/code> and make it depend on the libraries from <code>externals<\/code>:<\/p>\n<pre><code >\/\/ bundle.js\r\ndefine([&quot;\/libraries\/react.min.js&quot;, &quot;\/libraries\/react-dom.min.js&quot;], function () { \u2026 });\r\n<\/code><\/pre>\n<p>Then, the loader will load your libraries along with the bundle. The good thing here is that the libraries will be cached in the browser or in the loader cache \u2013 so they won\u2019t be loaded twice.<\/p>\n<h2 id=\"%cf%83-decrease-front-end-size\">\u03a3: Decrease front-end size<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#%cf%83-decrease-front-end-size\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<ul>\n<li style=\"margin-bottom: 0\">Configure minification\n<li style=\"margin-bottom: 0\">Pass <code>NODE_ENV=production<\/code> to the code\n<li style=\"margin-bottom: 0\">Use ECMAScript imports and exports\n<li style=\"margin-bottom: 0\">Drop unused locales in Moment.js\n<li style=\"margin-bottom: 0\">Drop unused methods in Lodash\n<li>Use <code>externals<\/code> if you have common libraries\n<\/ul>\n<h1 id=\"task-improve-caching\"><span class=\"task-badge\">Task<\/span> Improve caching<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#task-improve-caching\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p><em>Given:<\/em> you have a front-end application. You want to cache it better so that the visitor loads it faster and doesn\u2019t re-download the whole app when it\u2019s updated.<\/p>\n<h2 id=\"using-hash\">Using hash<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#using-hash\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>The default approach of doing caching is to tell the browser cache a file for a very long time (e.g. a year), and rename the file when changing it to force browser to re-download it:<\/p>\n<pre><code >&lt;!-- Before the change --&gt;\r\n&lt;script src=&quot;.\/index.js?version=15&quot;&gt;\r\n\r\n&lt;!-- After the change --&gt;\r\n&lt;script src=&quot;.\/index.js?version=16&quot;&gt;\r\n<\/code><\/pre>\n<p>Webpack also lets you do such thing. However, instead of versioning a file, it calculates the file hash which you can specify in the bundle name. In this case, each time you change the code, the file name will change, and the browser will re-download it:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'><a href='https:\/\/webpack.js.org\/concepts\/output\/#output-chunkfilename'>About <code>&#091;chunkhash&#093;<\/code>, <code>&#091;hash&#093;<\/code>, <code>&#091;name&#093;<\/code> and <code>&#091;id&#093;<\/code><\/a><\/div>\n<div class='annotated-element__content'>\n<pre><code >\/\/ webpack.config.js\r\nmodule.exports = {\r\n  entry: '.\/index.js',\r\n  output: {\r\n    filename: 'bundle.[chunkhash].js'\r\n       \/\/ \u2192 bundle.8e0d62a03.js\r\n  }\r\n};\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>The only remaining problem is how to get the file name to send it to the client. There are two solutions: <code>HtmlWebpackPlugin<\/code> and <code>WebpackManifestPlugin<\/code>.<\/p>\n<p><a href=\"https:\/\/github.com\/jantimon\/html-webpack-plugin\"><code>HtmlWebpackPlugin<\/code><\/a> is a more automated solution. During compilation, it generates an HTML file which includes all compiled resources. If your server logic is simple, then this plugin should be enough for you:<\/p>\n<pre><code class=\"html\">&lt;!-- index.html --&gt;\r\n&lt;!doctype html&gt;\r\n&lt;!-- ... --&gt;\r\n&lt;script src=&quot;bundle.8e0d62a03.js&quot;&gt;&lt;\/script&gt;\r\n<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/danethurber\/webpack-manifest-plugin\"><code>WebpackManifestPlugin<\/code><\/a> is a more flexible solution which is useful if you have a complex server part. It generates a JSON file with a mapping between file names without hash and file names with hash. You can use this JSON on your server:<\/p>\n<pre><code class=\"javascript\">{\r\n  &quot;bundle.js&quot;: &quot;bundle.8e0d62a03.js&quot;\r\n}\r\n<\/code><\/pre>\n<div class=\"snippet\">\n<h2 id=\"pitfall-hash-could-change-even-if-the-bundle-is-the-same\">Pitfall: hash could change even if the bundle is the same<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-hash-could-change-even-if-the-bundle-is-the-same\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'><a href='https:\/\/github.com\/jhnns\/webpack-hash-test'>A repository listing the problems with hash<\/a><\/div>\n<div class='annotated-element__content'>\n<p>The hash could change if you rename a file or compile the bundle under a different OS. This is a bug, and I was unable to find a workaround. You can see the discussion about the bug <a href=\"https:\/\/github.com\/webpack\/webpack\/issues\/1479\">on GitHub<\/a>.<\/p>\n<p><mark>Update:<\/mark> the previous version of this part recommended using <a href=\"https:\/\/www.npmjs.com\/package\/webpack-chunk-hash\"><code>webpack-chunk-hash<\/code><\/a> as a solution. Turns out, it doesn\u2019t help.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<h2 id=\"code-splitting\">Code splitting<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#code-splitting\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>The next way to improve caching is to split the bundle into smaller pieces.<\/p>\n<p>Imagine you have a large website, and you\u2019re compiling it into a single bundle:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55.png\" alt=\"\" width=\"1920\" height=\"1080\" class=\"alignnone size-full wp-image-186\" style=\"border: 1px solid #ccc;\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55.png 1920w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55-300x169.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55-768x432.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55-1024x576.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-09-55-1200x675.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>Each time you\u2019re changing a single module, the whole bundle gets recompiled. This means that even if you\u2019re changing the comments module, and a specific user is only visiting the main page, they\u2019ll still have to re-download the code for this page.<\/p>\n<p>If you split your bundle into several pieces \u2013 one for the main page and one for the article page \u2013 the user will only have to re-download the changed piece of code. Webpack lets you do this. In webpack terminology, these pieces of the bundle are called <em>chunks<\/em>.<\/p>\n<p>To split the code into chunks, you specify several entry points and do a few other changes. Here\u2019s the optimal webpack config:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You specify multiple <a href='https:\/\/webpack.js.org\/concepts\/entry-points\/'>entry points<\/a>, and webpack generates a separate chunk for each point. Each chunk will only include the dependencies it needs<\/div>\n<div class='annotated-element__content'>\n<pre><code >module.exports = {\r\n  entry: {\r\n    homepage: '.\/index.js',\r\n    article: '.\/article.js'\r\n  },\r\n  output: {\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You replace a fixed filename with <code>&#091;name&#093;<\/code>. <code>&#091;name&#093;<\/code> will correspond to the entry point name<\/div>\n<div class='annotated-element__content'>\n<pre><code >\u00a0\u00a0\u00a0\u00a0filename: '[name].[chunkhash].js'\r\n  },\r\n  plugins: [\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You add <a href='https:\/\/www.npmjs.com\/package\/webpack-manifest-plugin'><code>Webpack<wbr \/>Manifest<wbr \/>Plugin<\/code><\/a> and <a href='https:\/\/www.npmjs.com\/package\/webpack-chunk-hash'><code>Webpack<wbr \/>Chunk<wbr \/>Hash<\/code><\/a> \u2013 plugins from the previous section<\/div>\n<div class='annotated-element__content'>\n<pre><code >\u00a0\u00a0\u00a0\u00a0new WebpackManifestPlugin(),\r\n\u00a0\u00a0\u00a0\u00a0new WebpackChunkHash(),\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>\n<p>You add two <a href='https:\/\/webpack.js.org\/plugins\/commons-chunk-plugin\/'><code>Commons<wbr \/>Chunk<wbr \/>Plugin<\/code>s<\/a>. They let you move some code from existing chunks to new <em>commons chunks<\/em>.<\/p>\n<p>The first plugin moves all <code>node_modules<\/code> dependencies to a separate chunk. This allows you update the code without invalidating dependencies.<\/p>\n<p>The second plugin moves <em>webpack\u2019s runtime<\/em> to a separate chunk. This allows you to update runtime without invalidating other code. <em>Runtime<\/em> is a webpack\u2019s system code that is responsible for loading the app<\/p>\n<\/div>\n<div class='annotated-element__content'>\n<pre><code >\u00a0\u00a0\u00a0\u00a0new webpack.optimize.CommonsChunkPlugin({\r\n      name: 'vendor',\r\n      minChunks: m =&gt; m.context &amp;&amp;\r\n        m.context.includes('node_modules'),\r\n    }),\r\n\u00a0\u00a0\u00a0\u00a0new webpack.optimize.CommonsChunkPlugin({\r\n      name: 'runtime',\r\n      minChunks: Infinity,\r\n    }),\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You add <code>ashed<wbr \/>Module<wbr \/>Ids<wbr \/>Plugin<\/code>. By default, each module in webpack has an ID which corresponds to its order. If you add a new module, it can affect other module ids and invalidate the cached chunks. This plugin replaces order-based IDs with hash-based ones<\/div>\n<div class='annotated-element__content'>\n<pre><code >\u00a0\u00a0\u00a0\u00a0new webpack.HashedModuleIdsPlugin(),\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>\n<p>You add <a href='https:\/\/www.npmjs.com\/package\/chunk-manifest-webpack2-plugin'><code>Chunk<wbr \/>Manifest<wbr \/>Plugin<\/code><\/a>.<\/p>\n<p>By default, the webpack\u2019s runtime contains a mapping between IDs of chunks and their names. If you configure the file name to contain the hash, as we did with the <code>filename<\/code> option, the hash will change with each file change, and so will the runtime.<\/p>\n<p>ChunkManifestPlugin lets you extract this mapping into a separate JSON file. On the server, you\u2019ll need to inline this file into the global <code>webpackManifest<\/code> variable<\/p>\n<\/div>\n<div class='annotated-element__content'>\n<pre><code >\u00a0\u00a0\u00a0\u00a0new ChunkManifestPlugin({\r\n      filename: 'chunk-manifest.json',\r\n      manifestVariable: 'webpackManifest'\r\n    })\r\n  ]\r\n};\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>With this config, webpack will generate 6 files:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>Two separate entry points. Each should be loaded on the corresponding pages<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"nohighlight\">homepage.a68cd93e1a43281ecaf0.js\r\narticle.d07a1a5e55dbd86d572b.js\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>File with vendor dependencies and file with webpack runtime<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"nohighlight\">vendor.1ebfd76d9dbc95deaed0.js\r\nruntime.d41d8cd98f00b204e980.js\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>Two manifest files that you\u2019ll need on the server<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"nohighlight\">manifest.json\r\nchunk-manifest.json\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>And this is how often they\u2019ll change:<\/p>\n<ul>\n<li><code>homepage<\/code> and <code>article<\/code> \u2013 when the app code in these modules changes,\n<li><code>vendor<\/code> \u2013 when any dependencies of the app change,\n<li><code>runtime<\/code> \u2013 when webpack\u2019s runtime code changes (i.e. rarely and only with new webpack versions),\n<li><code>manifest.json<\/code> \u2013 when you add a new chunk \u2013 but that doesn\u2019t matter because this file is used in the server,\n<li><code>chunk-manifest.json<\/code> \u2013 on any code change \u2013 but that doesn\u2019t matter because this file is used in the server.\n<\/ul>\n<p>That\u2019s a bit more files, but it lets you effectively leverage long-term caching.<\/p>\n<h2 id=\"on-demand-code-splitting\">On-demand code splitting<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#on-demand-code-splitting\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>The next way to improve caching (and optimize time to first paint) is to load some parts of code on demand.<\/p>\n<p>Imagine you have a page with an article:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1.png\" alt=\"\" width=\"1920\" height=\"1080\" class=\"alignnone size-full wp-image-188\" style=\"border: 1px solid #ccc;\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1.png 1920w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1-300x169.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1-768x432.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1-1024x576.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/chrome_2017-05-09_20-21-29-1-1200x675.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>When opening this page, the visitor wants to see the content at first. Comments, sidebar and other parts of the page are less relevant to them. However, if you bundle all these blocks into a single file, the visitor will have to wait until the whole file is downloaded \u2013 with all the page modules. This isn\u2019t cool.<\/p>\n<p>Thankfully, webpack lets you optimize this by loading code on demand. You can specify that you want to load specific modules dynamically, and webpack will move them to separate chunks and download when they\u2019re required. This is how it works:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>You have an <code>article-page.js<\/code> file. When you compile it, the bundle receives all the code for articles, comments and sidebar<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"javascript\">\/\/ article-page.js\r\nimport { renderArticle } from '.\/components\/article';\r\nimport { renderComments } from '.\/components\/comments';\r\nimport { renderSidebar } from '.\/components\/sidebar';\r\n\r\nrenderArticle();\r\nrenderComments();\r\nrenderSidebar();\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>\u2193<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_yes annotated-element_mobile-direction_annotation-then-content'>\n<div class='annotated-element__annotation'>To load code on demand, you replace static <code>import<\/code> with dynamic <code>import()<\/code> calls. Webpack will move the code from <code>.\/comments.js<\/code> and <code>.\/sidebar.js<\/code> into separate chunks and load them when they\u2019re required<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"javascript\">\/\/ article-page.js\r\nimport { renderArticle } from '.\/components\/article';\r\nrenderArticle();\r\n\r\nimport('.\/comments.js')\r\n  .then((module) =&gt; { module.renderComments(); });\r\nimport('.\/sidebar.js')\r\n  .then((module) =&gt; { module.renderSidebar(); });\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>This change will improve the initial loading performance. Also, it will optimize caching because when you change the code that belongs to a specific chunk, other chunks won\u2019t get affected.<\/p>\n<p>The only thing left is to add chunk hashes to their names. This is done with <code>output.chunkFilename<\/code> option. This option is specific to chunks generated by on-demand code splitting:<\/p>\n<pre><code class=\"javascript\">\/\/ webpack.config.js\r\nmodule.exports = {\r\n  output: {\r\n    filename: '[name].[chunkhash].js',\r\n    chunkFilename: '[name].[chunkhash].js',\r\n  }\r\n};\r\n<\/code><\/pre>\n<div class=\"snippet\">\n<h2 id=\"pitfall-compiling-with-babel\">Pitfall: Compiling with Babel<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#pitfall-compiling-with-babel\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>If you compile this code with Babel with default presets, you\u2019ll have a syntax error: Babel don\u2019t understand <code>import()<\/code> out of the box. To prevent the error, add the <a href=\"https:\/\/www.npmjs.com\/package\/babel-plugin-syntax-dynamic-import\"><code>syntax-dynamic-import<\/code><\/a> plugin.\n<\/div>\n<h2 id=\"other-solutions\">Other solutions<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#other-solutions\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<p>There are a couple of other solutions that I haven\u2019t worked with but which should also bring benefits with caching:<\/p>\n<ul>\n<li><code>AggressiveSplittingPlugin<\/code> is a plugin that optimizes your code for HTTP\/2 by splitting each chunk into smaller chunks as much as possible. This greatly improves caching on the client side but slightly worsens the gzip compression. See <a href=\"https:\/\/github.com\/webpack\/webpack\/tree\/master\/examples\/http2-aggressive-splitting\">the example in the webpack repository<\/a>.\n<li><code>OfflinePlugin<\/code> is a plugin that\u2019s usually used for creating offline-ready apps. However, you can use it to improve caching too! The plugin generates a service worker that downloads all the site resources in the background. So when a visitor visits the site and then switches to a different page, they\u2019ll have all the necessary files already cached. See <a href=\"https:\/\/www.npmjs.com\/package\/offline-plugin\">the <code>OfflinePlugin<\/code> docs<\/a>.\n<\/ul>\n<h2 id=\"%cf%83-improve-caching\">\u03a3: Improve caching<a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#%cf%83-improve-caching\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h2>\n<ul>\n<li>Add hash to the name of your resources and make the server tell clients to cache the resources for a long time\n<li>Split your code into smaller chunks with different entries, on-demand code splitting and <code>AggressiveSplittingPlugin<\/code>\n<li>Try caching your resources in the background with <code>OfflinePlugin<\/code>\n<\/ul>\n<p id=\"subscribe\">The next part of the guide, \u201cSpeeding up build and improving the development workflow\u201d, is coming soon. Leave your email to know when it\u2019s out:<br \/>\n<small>(you\u2019ll receive an email about the next part of the guide + a couple of more webpack-related posts if I write them; no spam)<\/small><\/p>\n<div>\n        <link href=\"\/\/cdn-images.mailchimp.com\/embedcode\/horizontal-slim-10_7.css\" rel=\"stylesheet\" type=\"text\/css\">\n<style type=\"text\/css\"> #mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; margin: 0 -16px 28px; padding: 19px 15px; background: rgba(255, 219, 1, 0.25); border: 1px solid #ffdb01; border-radius: 4px; } <\/style>\n<div class=\"signup-form\" id=\"mc_embed_signup\">\n<form action=\"\/\/iamakulov.us15.list-manage.com\/subscribe\/post?u=d7d2af05c405078e8f6a48956&amp;id=d24b88eefe\" method=\"post\" id=\"mc-embedded-subscribe-form\" name=\"mc-embedded-subscribe-form\" class=\"validate\" target=\"_blank\" novalidate>\n<div id=\"mc_embed_signup_scroll\"><input type=\"email\" value=\"\" name=\"EMAIL\" class=\"email\" id=\"mce-EMAIL\" placeholder=\"email address\" required> <\/p>\n<div style=\"position: absolute; left: -5000px;\" aria-hidden=\"true\"><input type=\"text\" name=\"b_d7d2af05c405078e8f6a48956_d24b88eefe\" tabindex=\"-1\" value=\"\"><\/div>\n<div class=\"clear\"><input type=\"submit\" value=\"Subscribe\" name=\"subscribe\" id=\"mc-embedded-subscribe\" class=\"button\"><\/div>\n<\/div>\n<\/form>\n<\/div>\n<\/div>\n<style>\n.post-139 .webpack-navigator-2 {\n    margin: 0 -16px 1.75em;\n    padding: 19px 15px;\n    background: rgba(255, 219, 1, 0.25);\n    border: 1px solid #ffdb01;\n    border-radius: 4px;\n} .post-139 .webpack-navigator-2 p:last-child {\n  margin-bottom: 0;\n} .post-139 .webpack-navigator-2 li:not(:last-child) {\n  margin-bottom: 0;\n} .post-139 .task-badge {\n    font-weight: normal;\n    font-size: 0.7em;\n    border: 1px solid black;\n    padding: 2px 4px;\n    border-radius: 2px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    vertical-align: 2px;\n} .post-139 .image-link img {\n  border: 1px solid #007acc;\n} .post-139 .image-link:hover img, .post-139 .image-link:focus img, .post-139 .image-link:active img {\n  border: 1px solid #686868;\n} .post-139 p:empty {\n  display: none\n} .post-139 .webpack-navigator-2 p:empty {\n  display: block;\n}\n<\/style>\n","protected":false},"excerpt":{"rendered":"<p>This is the second part of a three-part introduction into webpack: Bundling front-end and adding compilation Decreasing front-end size and improving caching (you are here!) Speeding up build and improving the development workflow Want to stay tuned for the future posts? Subscribe Last updated on 21 Jan 2018: replaced the recommended plugin in the Moment.js &hellip; <a href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;webpack for real tasks: decreasing front-end size and improving caching&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[15,12],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v23.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>webpack for real tasks: decreasing front-end size and improving caching - Ivan Akulov\u2019s blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"webpack for real tasks: decreasing front-end size and improving caching\" \/>\n<meta property=\"og:description\" content=\"This part of the guide teaches you how to improve the loading performance of your front-end app. The post also includes several non-obvious pitfalls which I\u2019ve personally fought with.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\" \/>\n<meta property=\"og:site_name\" content=\"Ivan Akulov\u2019s blog\" \/>\n<meta property=\"article:publisher\" content=\"http:\/\/facebook.com\/iamakulov.page\" \/>\n<meta property=\"article:published_time\" content=\"2017-05-09T20:15:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-01-21T08:51:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/cover2-new-3.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ivan Akulov\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:site\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ivan Akulov\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"19 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\",\"url\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\",\"name\":\"webpack for real tasks: decreasing front-end size and improving caching - Ivan Akulov\u2019s blog\",\"isPartOf\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png\",\"datePublished\":\"2017-05-09T20:15:21+00:00\",\"dateModified\":\"2018-01-21T08:51:47+00:00\",\"author\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81\"},\"breadcrumb\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage\",\"url\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png\",\"contentUrl\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png\",\"width\":1900,\"height\":450},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/iamakulov.com\/notes\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"webpack for real tasks: decreasing front-end size and improving caching\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#website\",\"url\":\"https:\/\/iamakulov.com\/notes\/\",\"name\":\"Ivan Akulov\u2019s blog\",\"description\":\"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/iamakulov.com\/notes\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81\",\"name\":\"Ivan Akulov\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g\",\"caption\":\"Ivan Akulov\"},\"description\":\"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.\",\"sameAs\":[\"http:\/\/iamakulov.com\",\"https:\/\/x.com\/iamakulov\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"webpack for real tasks: decreasing front-end size and improving caching - Ivan Akulov\u2019s blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/","og_locale":"en_US","og_type":"article","og_title":"webpack for real tasks: decreasing front-end size and improving caching","og_description":"This part of the guide teaches you how to improve the loading performance of your front-end app. The post also includes several non-obvious pitfalls which I\u2019ve personally fought with.","og_url":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/","og_site_name":"Ivan Akulov\u2019s blog","article_publisher":"http:\/\/facebook.com\/iamakulov.page","article_published_time":"2017-05-09T20:15:21+00:00","article_modified_time":"2018-01-21T08:51:47+00:00","og_image":[{"width":2000,"height":1200,"url":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/cover2-new-3.png","type":"image\/png"}],"author":"Ivan Akulov","twitter_card":"summary_large_image","twitter_creator":"@iamakulov","twitter_site":"@iamakulov","twitter_misc":{"Written by":"Ivan Akulov","Est. reading time":"19 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/","url":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/","name":"webpack for real tasks: decreasing front-end size and improving caching - Ivan Akulov\u2019s blog","isPartOf":{"@id":"https:\/\/iamakulov.com\/notes\/#website"},"primaryImageOfPage":{"@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage"},"image":{"@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage"},"thumbnailUrl":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png","datePublished":"2017-05-09T20:15:21+00:00","dateModified":"2018-01-21T08:51:47+00:00","author":{"@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81"},"breadcrumb":{"@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#primaryimage","url":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png","contentUrl":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2017\/05\/POWERPNT_2017-05-09_19-17-35-2.png","width":1900,"height":450},{"@type":"BreadcrumbList","@id":"https:\/\/iamakulov.com\/notes\/webpack-front-end-size-caching\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/iamakulov.com\/notes\/"},{"@type":"ListItem","position":2,"name":"webpack for real tasks: decreasing front-end size and improving caching"}]},{"@type":"WebSite","@id":"https:\/\/iamakulov.com\/notes\/#website","url":"https:\/\/iamakulov.com\/notes\/","name":"Ivan Akulov\u2019s blog","description":"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/iamakulov.com\/notes\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81","name":"Ivan Akulov","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g","caption":"Ivan Akulov"},"description":"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.","sameAs":["http:\/\/iamakulov.com","https:\/\/x.com\/iamakulov"]}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/139"}],"collection":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/comments?post=139"}],"version-history":[{"count":66,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/139\/revisions"}],"predecessor-version":[{"id":923,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/139\/revisions\/923"}],"wp:attachment":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/media?parent=139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/categories?post=139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/tags?post=139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}