- 1. Webpack 4 course – part one. Entry, output and ES6 modules
- 2. Webpack 4 course – part two. Using loaders to handle scss, image files and transpile JS
- 3. Webpack 4 course – part three. Working with plugins
- 4. Webpack 4 course – part four. Code splitting with SplitChunksPlugin
- 5. Webpack 4 course – part five. Built-in optimization for production
- 6. Webpack 4 course – part six. Increasing development experience
- 7. Webpack 4 course – part seven. Decreasing the bundle size with tree shaking
- 8. Webpack 4 course – part eight. Dynamic imports with prefetch and preload
Today we will cover the development value of the mode property. It will automatically set the webpack configuration for you to make the development easier. Aside from that, we will also cover the wepback-dev-server and webpack-serve – Hot Module Replacement included. Let’s go!
Webpack 4 course: a development server
A one step forward to a better development experience is running webpack in the watch mode. Try running webpack --watch. Now, every time you make some changes, Webpack will rebuild your project and outputs he code. Webpack-dev-server does that and more. Instead of writing files to the destination directory, it handles them in memory. After building, the output is served locally.
Running webpack-dev-server
The first thing to do is to install it.
1 |
npm install webpack-dev-server |
The second one is to add it to the scripts in your package.json
1 2 3 4 |
"scripts": { "build": "webpack", "start": "webpack-dev-server" } |
Now to run it, just type npm start. You will be greeted with a message such as:
1 |
「wds」: Project is running at http://localhost:8080/ |
The only thing left is to open http://localhost:8080/ in your browser.
Every time you make a change to your code, the site gets updated and after refreshing the page, you can see your changes.
Hot Module Replacement
To make your development experience even better, you can skip the need to refresh the page with the Hot Module Replacement. For example, when you make a change to some of your styles, you don’t need to reload the whole application to see the changes.
In the fourth part of the course we’ve used MiniCssExtractPlugin. Please note, that in the moment of writing this article, Hot Module Reload support for MiniCssExtractPlugin is still yet to come. For more information visit this GitHub issue. For now you might want to stick for the style-loader while in the development environment.
When you run webpack-dev-server it uses the same configuration file as a regular build. There is a parameter that you can add to your webpack.config.js that allows you some additional options. It is called devServer. We need it to enable the Hot Module Replacement.
1 2 3 |
devServer: { hot: true, } |
The last thing left to do is to add webpack.HotModuleReplacementPlugin to your plugins.
webpack.config.js
1 2 3 4 5 6 7 8 9 10 |
const webpack = require('webpack'); module.exports = { devServer: { hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ] } |
Note that running webpack-dev-server with the –hot flag would also add HotModuleReplacementPlugin to your plugins. Doing it twice could give you problems.
Works like a charm with our CSS, but when it comes to the JavaScript, it needs one additional step.
index.js
1 2 3 4 5 6 7 |
import { divide } from "./divide"; console.log(`6 / 2 = ${divide(6,2)}`); if(module.hot) { module.hot.accept(); } |
divide.js
1 2 3 |
export function divide(a, b) { return a/b; } |
Running module.hot.accept() will make the module replaceable. This also applies to all other modules that it imports. With the code above it means that running accept() in index.js makes the divide module replaceable also.
The function module.hot.accept() can be ran with additional configuration. If you are interested, check out the documentation.
You will encounter some problems when using chunkhash in your output filename while using HotModuleReplacementPlugin. It might be a good idea to use HotModuleReplacementPlugin only with the development server (and not using chunkhash then).
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const isDevServer = require.main.filename.includes('webpack-dev-server'); const plugins = [ new HtmlWebpackPlugin({ template: './src/index.html' }), ]; if(isDevServer) { plugins.push(new webpack.HotModuleReplacementPlugin()); } module.exports = { output: { filename: isDevServer ? '[name].bundle.js' : '[name].[chunkhash].bundle.js', path: path.resolve(__dirname, 'dist'), }, plugins, devServer: { hot: true } } |
webpack-serve
Even though webpack-dev-server was used for quite some time, now it is maintenance-only mode and will not be accepting any additional features anytime soon. Because of that, it might be a good idea to check out its successor: the webpack-serve. A thing to keep in mind is that it uses WebSockets. If you are wondering if the browsers that you want to test the project on are compatible, check the compatibility table.
1 |
npm install webpack-serve |
package.json
1 2 3 4 |
"scripts": { "build": "webpack", "start": "webpack-serve" } |
A very important note is that it uses Hot Module Replacement by default, so adding webpack.HotModuleReplacementPlugin would result in an error.
The webpack-serve is relatively new compared to webpack-dev-server, but it is definitely worth checking out.
Update:
It turned out that the webpack-serve is not going to be developed anymore and is now deprecated.
We should stick to the webpack-dev-server.
mode: “development”
In the previous part of the course, we’ve covered the production value of the mode parameter. This is the time for the development. Let’s go through what it does for us.
DefinePlugin
As said previously, this plugin allows you to create global constants resolved at compile time.
Since this plugin is used also in the mode:production, for a more detailed explanation check out Webpack 4 course – part five. Built-in optimization for production
This time the value is "process.env.NODE_ENV": JSON.stringify("development") :
1 2 3 4 5 6 7 8 9 |
module.exports = { mode: "development", // using mode: "development" attaches the following configuration: plugins: [ new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }), ] } |
NamedModulesPlugin
This is another plugin that is added when using mode: "development". It is useful while using Hot Module Replacement. Thanks to NamedModulesPlugin we can see the relative path of the replaced module.
1 2 3 4 5 6 |
[WDS] App updated. Recompiling... [WDS] App hot update... [HMR] Checking for updates on the server... [HMR] Updated modules: [HMR] - ./src/style.css [HMR] App is up to date |
Otherwise, we would just see an id of the module instead of, for example, ./src/style.css.
NamedChunksPlugin
It serves a little similar purpose to the NamedModulesPlugin. Thanks to NamedChunksPlugin, not only your modules will have a name, but also chunks. You can look them up in the window.webpackJsonp property when your application runs in the browser.
An additional advantage to NamedModulesPlugin and NamedChunksPlugin is that you no longer use ids that can change with adding and removing dependencies. Since ids or names are used in actual output files, changing them will change the hash of the file, even if the content of the module did not change. Using NamedModulesPlugin and NamedChunksPlugin will help you deal with caching in browsers. Let’s compare some output code.
Without NamedModulesPlugin and NamedChunksPlugin:
1 2 3 4 5 6 7 |
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[2],{ /***/ 6: (...) // divide.js module output code /***/ 7: (...) // substract.js module output code ]); |
Using NamedModulesPlugin and NamedChunksPlugin:
1 2 3 4 5 6 7 |
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["utilities~main"],{ /***/ "./src/utilities/divide.js": (...) // divide.js module output code /***/ "./src/utilities/substract.js": (...) // substract.js module output code ]); |
Devtool
Aside from adding some plugins, setting mode: "production" does one more thing. It enables Source Maps by setting the value of devtool to eval.
1 2 3 4 5 |
module.exports = { mode: "development", // using mode: "development" attaches the following configuration: devtool: "eval" } |
Transpiling, uglifying and bundling your code can make the life of your users easier. After this process, the code is smaller and performs better. Debugging such code would be really difficult though. Because of that, the Source Maps were introduced. They map your output code with their source files. Thanks to that, you can use the debugger easier and set breakpoints while looking in the actual code and not the one that was served to the browsers. We will dig deeper into source maps in the upcoming parts of the course, but if you feel the need to customize it now, check out the documentation.
Summary
Webpack is a great tool for developing modern web applications. It not only lets you optimize your code for production but also can be customized in a way that makes your development experience better. This time we’ve covered running a development server and the development value of the mode property. We’ve also learned how to use Hot Module Replacement. All of these things combined can greatly help you develop your applications easier and faster.
Please update this article about the webpack-serve, that is deprecated and nowadays the webpack-dev-server is the plugin in use. See: https://github.com/webpack-contrib/webpack-serve
I updated the course accordingly. Thanks for contributing!
FYI, webpack-serve has now itself been deprecated and we’re all supposed to switch back to webpack-dev-server. See https://github.com/webpack-contrib/webpack-serve/blob/master/README.md. I guess this sort of thing is par for the course in javascript development…
Thank you for contributing, I updated the course.
Hi there. Consider updating “start”: “webpack-dev-sever” to “start”: “webpack-dev-seRver”. There’s a spelling error in there =)
And THANK YOU for the amazing article/series.
I am totally loving it!
Thank you! I fixed it.