├── README.md
└── recipes
└── css.md
/README.md:
--------------------------------------------------------------------------------
1 | # Webpack tricks
2 |
3 | Just a small catalog of Webpack tips and tricks I've learned. These tricks work with Webpack v3 unless otherwise specified.
4 |
5 | Table of contents
6 | -----------------
7 |
8 | * [Progress reporting](#progress-reporting)
9 | * [Minification](#minification)
10 | * [Multiple bundles](#multiple-bundles)
11 | * [Split app and vendor code](#split-app-and-vendor-code)
12 | * [Source maps (Webpack 1)](#source-maps-webpack-1)
13 | * [Source maps (Webpack 2-3)](#source-maps-webpack-2-3)
14 | * [Output CSS files](#output-css-files)
15 | * [Development mode](#development-mode)
16 | * [Investigating bundle sizes](#investigating-bundle-sizes)
17 | * [Smaller React](#smaller-react)
18 | * [Smaller Lodash](#smaller-lodash)
19 | * [Requiring all files in a folder](#requiring-all-files-in-a-folder)
20 | * [Clean up extract-text-webpack-plugin log](#clean-up-extract-text-webpack-plugin-log)
21 |
22 | Progress reporting
23 | ------------------
24 |
25 | Invoke Webpack with:
26 |
27 | ```
28 | --progress --colors
29 | ```
30 |
31 | Minification
32 | ------------
33 |
34 | Invoke Webpack with `-p` for production builds. In Webpack 2, this also automatically sets `process.env.NODE_ENV === 'production'`.
35 |
36 | ```js
37 | webpack -p
38 | ```
39 |
40 | Multiple bundles
41 | ----------------
42 |
43 | Export multiple bundles by setting the output to `[name].js`. This example produces `a.js` and `b.js`.
44 |
45 | ```js
46 | module.exports = {
47 | entry: {
48 | a: './a',
49 | b: './b'
50 | },
51 | output: { filename: '[name].js' }
52 | }
53 | ```
54 |
55 | Concerned about duplication? Use the [CommonsChunkPlugin](https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin) to move the common parts into a new output file.
56 |
57 | ```js
58 | plugins: [ new webpack.optimize.CommonsChunkPlugin('init.js') ]
59 | ```
60 |
61 | ```html
62 |
63 |
64 | ```
65 |
66 | Split app and vendor code
67 | -------------------------
68 |
69 | Use CommonsChunkPlugin to move vendor code into `vendor.js`.
70 |
71 | ```js
72 | var webpack = require('webpack')
73 |
74 | module.exports = {
75 | entry: {
76 | app: './app.js',
77 | vendor: ['jquery', 'underscore', ...]
78 | },
79 |
80 | output: {
81 | filename: '[name].js'
82 | },
83 |
84 | plugins: [
85 | new webpack.optimize.CommonsChunkPlugin('vendor')
86 | ]
87 | }
88 | ```
89 |
90 | How this works:
91 |
92 | - We make a `vendor` entry point and load it with some libraries
93 | - CommonsChunkPlugin will remove these libraries from `app.js` (because it appears in 2 bundles now)
94 | - CommonsChunkPlugin also moves the Webpack runtime into `vendor.js`
95 |
96 | > Reference: [Code splitting](https://webpack.github.io/docs/code-splitting.html#split-app-and-vendor-code)
97 |
98 | Source maps (Webpack 1)
99 | -----------------------
100 |
101 | The best source maps option is `cheap-module-eval-source-map`. This shows original source files in Chrome/Firefox dev tools. It's faster than `source-map` and `eval-source-map`.
102 |
103 | ```js
104 | // Webpack 1 only
105 | const DEBUG = process.env.NODE_ENV !== 'production'
106 |
107 | module.exports = {
108 | debug: DEBUG ? true : false,
109 | devtool: DEBUG ? 'cheap-module-eval-source-map' : 'hidden-source-map'
110 | }
111 | ```
112 |
113 | Your files will now show up in Chrome Devtools as `webpack:///foo.js?a93h`. We want this to be cleaner like `webpack:///path/to/foo.js`.
114 |
115 | ```js
116 | output: {
117 | devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]'
118 | }
119 | ```
120 |
121 | > Reference: [devtool documentation](https://webpack.github.io/docs/configuration.html#devtool)
122 |
123 | Source maps (Webpack 2-3)
124 | -------------------------
125 |
126 | The best source maps option is `cheap-module-source-map`. The cheap-module-eval-source-map strategy no longer shows correct traces in Chrome/Firefox.
127 |
128 | ```js
129 | // Webpack 2 only
130 | const DEBUG = process.env.NODE_ENV !== 'production'
131 |
132 | module.exports = {
133 | devtool: DEBUG ? 'cheap-module-source-map' : 'hidden-source-map'
134 | }
135 | ```
136 |
137 | If you're using [extract-text-webpack-plugin](https://www.npmjs.com/package/extract-text-webpack-plugin), use `'source-map'` instead. CSS sourcemaps won't work otherwise.
138 |
139 | ```js
140 | // Only if you're using extract-text-webpack-plugin
141 | module.exports = {
142 | devtool: DEBUG ? 'source-map' : 'hidden-source-map'
143 | }
144 | ```
145 |
146 | Your files will now show up in Chrome Devtools as `webpack:///foo.js?a93h`. We want this to be cleaner like `webpack:///path/to/foo.js`.
147 |
148 | ```js
149 | output: {
150 | devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]'
151 | }
152 | ```
153 |
154 | > Reference: [devtool documentation](https://webpack.js.org/configuration/devtool/#devtool)
155 |
156 | Output CSS files
157 | ----------------
158 |
159 | This is complicated, and the guide [can be found here](recipes/css.md).
160 |
161 | Development mode
162 | ----------------
163 |
164 | Want to have certain options only appear in development mode?
165 |
166 | ```js
167 | const DEBUG = process.env.NODE_ENV !== 'production'
168 |
169 | // Webpack 1
170 | module.exports = {
171 | debug: DEBUG ? true : false,
172 | devtool: DEBUG ? 'cheap-module-eval-source-map' : 'hidden-source-map'
173 | }
174 |
175 | // Webpack 2
176 | module.exports = {
177 | devtool: DEBUG ? 'cheap-module-source-map' : 'hidden-source-map'
178 | }
179 | ```
180 |
181 | __Webpack 1:__ Be sure to invoke Webpack as `env NODE_ENV=production webpack -p` when building your production assets.
182 |
183 | __Webpack 2:__ Invoke Webpack as `webpack -p` when building your production assets. `NODE_ENV` is automatically set by Webpack.
184 |
185 | Investigating bundle sizes
186 | --------------------------
187 |
188 | Want to see what dependencies are the largest? You can use webpack-bundle-size-analyzer.
189 |
190 | ```js
191 | $ yarn global add webpack-bundle-size-analyzer
192 |
193 | $ ./node_modules/.bin/webpack --json | webpack-bundle-size-analyzer
194 | jquery: 260.93 KB (37.1%)
195 | moment: 137.34 KB (19.5%)
196 | parsleyjs: 87.88 KB (12.5%)
197 | bootstrap-sass: 68.07 KB (9.68%)
198 | ...
199 | ```
200 |
201 | If you're generating source maps (you should), you can also use source-map-explorer, which also works outside of Webpack.
202 |
203 | ```js
204 | $ yarn global add source-map-explorer
205 |
206 | $ source-map-explorer bundle.min.js bundle.min.js.map
207 | ```
208 |
209 | > Reference: [webpack-bundle-size-analyzer](https://github.com/robertknight/webpack-bundle-size-analyzer), [source-map-explorer](https://www.npmjs.com/package/source-map-explorer)
210 |
211 | Smaller React
212 | -------------
213 |
214 | React will build dev tools by default. You don't need this in production. Use the EnvironmentPlugin to make these dev tools disappear. This saves you around 30kb.
215 |
216 | ```js
217 | plugins: [
218 | new webpack.EnvironmentPlugin({
219 | NODE_ENV: 'development'
220 | })
221 | ]
222 | ```
223 |
224 | __Webpack 1:__ Be sure to invoke Webpack as `env NODE_ENV=production webpack -p` when building your production assets.
225 |
226 | __Webpack 2:__ Invoke Webpack as `webpack -p` when building your production assets. `NODE_ENV` is automatically set by Webpack.
227 |
228 | > Reference: [EnvironmentPlugin documentation](https://webpack.js.org/plugins/environment-plugin/)
229 |
230 | Smaller Lodash
231 | --------------
232 |
233 | [Lodash](https://lodash.com/) is very useful but usually we only need a small part of its full functionality. [lodash-webpack-plugin](https://github.com/lodash/lodash-webpack-plugin) can help you shrink the lodash build by replacing [feature sets](https://github.com/lodash/lodash-webpack-plugin#feature-sets) of modules with [noop](https://lodash.com/docs#noop), [identity](https://lodash.com/docs#identity), or simpler alternatives.
234 |
235 | ```js
236 | const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
237 |
238 | const config = {
239 | plugins: [
240 | new LodashModuleReplacementPlugin({
241 | path: true,
242 | flattening: true
243 | })
244 | ]
245 | };
246 | ```
247 |
248 | This may save you >10kb depending on how much you use lodash.
249 |
250 | Requiring all files in a folder
251 | -------------------------------
252 |
253 | Ever wanted to do this?
254 |
255 | ```js
256 | require('./behaviors/*') /* Doesn't work! */
257 | ```
258 |
259 | Use require.context.
260 |
261 | ```js
262 | // http://stackoverflow.com/a/30652110/873870
263 | function requireAll (r) { r.keys().forEach(r) }
264 |
265 | requireAll(require.context('./behaviors/', true, /\.js$/))
266 | ```
267 |
268 | > Reference: [require.context](http://webpack.github.io/docs/context.html#require-context)
269 |
270 | Clean up extract-text-webpack-plugin log
271 | ----------------------------------------
272 |
273 | If you're seeing this in your debug log when using [extract-text-webpack-plugin](https://www.npmjs.com/package/extract-text-webpack-plugin):
274 |
275 | ```
276 | Child extract-text-webpack-plugin:
277 | + 2 hidden modules
278 | Child extract-text-webpack-plugin:
279 | + 2 hidden modules
280 | Child extract-text-webpack-plugin:
281 | + 2 hidden modules
282 | ```
283 |
284 | Turn it off using `stats: { children: false }`.
285 |
286 | ```js
287 | /* webpack.config.js */
288 | stats: {
289 | children: false,
290 | },
291 | ```
292 |
293 | > Reference: [extract-text-webpack-plugin#35](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/35)
294 |
--------------------------------------------------------------------------------
/recipes/css.md:
--------------------------------------------------------------------------------
1 | Output CSS files
2 | ================
3 |
4 | > __NOTE:__ This guide only applies to Webpack 2.x.
5 |
6 | Webpack supports CSS via [css-loader](https://www.npmjs.com/package/css-loader) and [style-loader](https://www.npmjs.com/package/style-loader). You can then use `require('./style.css')` with some minimal configuration. I personally don't find loading CSS via JS an ideal setup, so here's a complicated way to output `.css` files with source maps in development.
7 |
8 | > __NOTE:__ This is complicated and hard to follow along. Check [rstacruz/webpack-starter-kit](https://github.com/rstacruz/webpack-starter-kit) for an example.
9 |
10 | ## What you'll need
11 |
12 | You'll need [ExtractTextWebpackPlugin](https://www.npmjs.com/package/extract-text-webpack-plugin). Let's set up a `DEBUG` constant as described in [§ Development mode](../README.md#development-mode) so we can set up source maps only for development.
13 |
14 | ```js
15 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
16 | const DEBUG = process.env.NODE_ENV !== 'production'
17 | ```
18 |
19 | ## Entry point
20 |
21 | First define an entry point for your CSS. This will generate `assets/css/app.css` based on `web/css/app.css`.
22 |
23 | ```js
24 | entry: {
25 | 'assets/css/app': './web/css/app.css'
26 | },
27 | ```
28 |
29 | ## Module rule
30 |
31 | Add a module rule to parse `*.css` files via ExtractTextPlugin, `postcss-loader` and `css-loader`. The `DEBUG` magic here will only enable source maps in development mode.
32 |
33 | ```js
34 | module: {
35 | rules: [
36 | {
37 | test: /\.css$/,
38 | use: ExtractTextPlugin.extract({
39 | fallback: 'style-loader',
40 | use: [
41 | `css-loader?-url${DEBUG ? '&sourceMap&importLoaders=1' : ''}`,
42 | `postcss-loader${DEBUG ? '?sourceMap=inline' : ''}`
43 | ]
44 | })
45 | }
46 | ]
47 | }
48 | ```
49 |
50 | ## Extract to .css
51 |
52 | The entry point actually generates `assets/css/app.js` (JavaScript), but we want it as `app.css`. By using ExtractTextPlugin, you can move the CSS parts into a CSS file.
53 |
54 | ```js
55 | plugins: [
56 | new ExtractTextPlugin({
57 | filename: '[name].css',
58 | allChunks: true // preserve source maps
59 | }),
60 | ]
61 | ```
62 |
63 | ## Source maps
64 |
65 | You need to use `devtool: 'source-map'` for CSS source maps to work.
66 |
67 | ```js
68 | devtool: DEBUG ? 'source-map' : 'hidden-source-map',
69 | ```
70 |
71 | ## PostCSS configuration
72 |
73 | You most likely want to use PostCSS. Even if you use Less or Sass, PostCSS will let you do autoprefixing and other magic. You'll need a `postcss.config.js` configuration. In this example, I'm adding [postcss-cssnext](https://www.npmjs.com/package/postcss-cssnext), which adds automatic vendor prefixes and transpiling for support for future CSS features.
74 |
75 | ```js
76 | // postcss.config.js
77 | const DEBUG = process.env.NODE_ENV !== 'production'
78 |
79 | module.exports = {
80 | plugins: [
81 | require('postcss-import')(),
82 | require('postcss-cssnext')(),
83 | ]
84 | }
85 | ```
86 |
--------------------------------------------------------------------------------