- Introduction
- Setup Webpack Project with Django
- Load Webpack bundles in Django
- Linting in Webpack
- Load Webpack hash bundle in Django
- Code splitting with Webpack
- How to config HMR with Webpack and Django
- How to use HtmlWebpackPlugin to load Webpack bundle in Django
If you want a quick start with Webpack and Django, please check python-webpack-boilerplate
Objective
By default, Django devserver will reload if we change .py file of the project.
In this blog, let's add the live reloading feature to the frontend project, if we edit SCSS or JS, the page will reload automatically.
By the end of this chapter, you should be able to:
- Learn to make
webpack-dev-serverwork smoothly with Django - Understand what is Hot Module Replacement (HMR) and the workflow
Advantage
In the previous chapter, we are using Webpack watch mode. (webpack can watch files and recompile whenever they change)
So some people might ask, why do we need webpack-dev-server?
webpack-dev-server will launch a server process, which makes
live reloadingpossible.
So the whole workflow can work like this:
- We edit
jsorscssin the editor. - The
webpack-dev-serverdetect the change, it recompile the files. And send message to the browser. (It is built on WebSocket and I will talk about in a bit) - Browser refresh the page automatically to let us see the change in realtime. (We do not have to manually refresh the page anymore)
Next, let's migrate from webpack watch mode to server mode.
Config
Update frontend/package.json
"start": "webpack serve --config webpack/webpack.config.dev.js"
We changed the start command to webpack server so it would launch webpack-dev-server
Update webpack/webpack.config.dev.js
const Path = require('path');
const Webpack = require('webpack');
const { merge } = require('webpack-merge');
const StylelintPlugin = require('stylelint-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-cheap-source-map',
output: {
chunkFilename: 'js/[name].chunk.js',
publicPath: 'http://localhost:9091/',
},
devServer: {
inline: true,
hot: true,
port: 9091,
},
plugins: [
new Webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
new StylelintPlugin({
files: Path.join('src', '**/*.s?(a|c)ss'),
}),
new MiniCssExtractPlugin({filename: 'css/app.css',})
],
module: {
rules: [
{
test: /\.js$/,
include: Path.resolve(__dirname, '../src'),
enforce: 'pre',
loader: 'eslint-loader',
options: {
emitWarning: true,
},
},
{
test: /\.html$/i,
loader: 'html-loader',
},
{
test: /\.js$/,
include: Path.resolve(__dirname, '../src'),
loader: 'babel-loader',
},
{
test: /\.s?css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader?sourceMap=true', 'postcss-loader', 'sass-loader'],
},
],
},
});
Notes:
- We can add config of the
webpack-dev-serverin thedevServer - Here we set the
port: 9091to specify the port the server is listening - Please note we changed
publicPathfrom/static/tohttp://localhost:9091/. Because now the bundle files are served bywebpack-dev-server
(frontend)$ npm run start
# run in another terminal
(frontend)$ cat webpack-stats.json
{
"status": "done",
"chunks": {
"app": [
"js/jquery.js",
"js/vendors.js",
"css/app.css",
"js/app.js"
]
},
"publicPath": "http://localhost:9091/",
"assets": {
"css/app.css": {
"name": "css/app.css",
"publicPath": "http://localhost:9091/css/app.css"
},
"js/app.js": {
"name": "js/app.js",
"publicPath": "http://localhost:9091/js/app.js"
},
"js/jquery.js": {
"name": "js/jquery.js",
"publicPath": "http://localhost:9091/js/jquery.js"
},
"js/vendors.js": {
"name": "js/vendors.js",
"publicPath": "http://localhost:9091/js/vendors.js"
},
"public/.gitkeep": {
"name": "public/.gitkeep",
"publicPath": "http://localhost:9091/public/.gitkeep"
},
"index.html": {
"name": "index.html",
"publicPath": "http://localhost:9091/index.html"
}
}
}
Notes:
- The bundle files are server on
http://localhost:9091 - We can test by visiting
http://localhost:9091/js/app.jsin the browser. - We can also check
http://localhost:9091/webpack-dev-serverto see what files are served bywebpack-dev-server - If we check the
frontend/builddirectory, we can see the directory is empty. I will talk about it in next section.
writeToDisk
By default, the webpack-dev-server would serve files from memory, which means, we can not see the bundle files on the disk. (frontend/build in this project)
However, we can change this behavior to make it work the same as webpack watch mode.
Update webpack/webpack.config.dev.js
devServer: {
inline: true,
hot: true,
port: 9091,
writeToDisk: true,
},
- We set
devServer.writeToDisktotrue
Now if we rerun the npm run start, we can see the files in frontend/build directory.
./build
├── css
│ └── app.css
├── index.html
├── js
│ ├── app.js
│ ├── jquery.js
│ └── vendors.js
└── public
Hot Module Replacement (HMR)
Workflow
The HMR feature is built on WebSocket.
WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.
- With WebSocket, the browser and server would establish a long-lived connection. They can send or receive message between each other.
- If
webpack-dev-serverdetect code change, it would recompile and send message to the browser. - Then browser would refresh the web page after receiving the message.
Config
Update webpack/webpack.config.dev.js
const Path = require('path');
const Webpack = require('webpack');
const { merge } = require('webpack-merge');
const StylelintPlugin = require('stylelint-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
target: 'web',
mode: 'development',
devtool: 'inline-cheap-source-map',
output: {
chunkFilename: 'js/[name].chunk.js',
publicPath: 'http://localhost:9091/',
},
devServer: {
inline: true,
hot: true,
port: 9091,
writeToDisk: true,
},
// ...
});
Notes:
- Here we add
target: 'web', we do this because of the bug of webpack-dev-server - The
inline: true,is needed (a script will be inserted in your bundle to take care of live reloading) - The
hot: true,indevServermeans the HMR feature is enabled withwebpack-dev-server.
Next, let's check how HMR work with webpack-dev-server.
- Rerun
npm run start - Open devtool in the browser.
- Click the
networktab so you can check the web requests. - In
networktab, click theWS(WebSocket) - Visit
http://localhost:9091in browser. (The html come fromsrc/index.htmland I will talk about it later)
We would see something like this

Notes:
- A websocket
ws://localhost:9091/sockjs-node/346/dtoksgqu/websockethas been established. - In the
messages, we can see the detailed messages from the webpack-dev-server.
Next, let's update src/scripts/index.js
import "../styles/index.scss";
import $ from "jquery/dist/jquery.slim";
import "bootstrap/dist/js/bootstrap.bundle";
$(document).ready(function () {
window.console.log("dom ready from michaelyin");
});
Notes:
- We changed the success message in
window.console.log - We will see the web page refreshed automatically and
dom ready from michaelyinshow up on theconsoleof the browserdevtool
HMR with Django
Now we already have a good understanding of HMR and let's make it work with Django.
(env)$ ./manage.py runserver
If we try to change src/index.js and see if it works, we get exception from the console of the browser devtool.
Access to fetch at 'http://localhost:9091/app.5c361815e51c8082dd52.hot-update.json' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Notes:
- Here we try to fetch some file on
localhost:9091from origin127.0.0.1:8000in browser. - The browser denied this because this is risky.
- You can check Cross-origin resource sharing to learn more.
Update webpack/webpack.config.dev.js
devServer: {
inline: true,
hot: true,
port: 9091,
writeToDisk: true,
headers: {
"Access-Control-Allow-Origin": "*",
}
},
Notes:
- We added
"Access-Control-Allow-Origin": "*"to theheaderssoCORS errorwould be resolved.
Now rerun npm run start, and update src/styles/index.scss
$primary: red;
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
...
Notes:
- This time, we update the
index.scssand see if it works - We add
$primary: red;to the top. - We can see the button color changed automatically.
The console of the browser devtool also have output like this
[HMR] Updated modules:
[HMR] - ./src/styles/index.scss
[HMR] App is up to date.
[HMR] Reload all css
If we check the network tab carefully, we will see the web page is not full reloaded.
Conclusion
You can also check Hot Module Replacement to learn more about HMR.
- Introduction
- Setup Webpack Project with Django
- Load Webpack bundles in Django
- Linting in Webpack
- Load Webpack hash bundle in Django
- Code splitting with Webpack
- How to config HMR with Webpack and Django
- How to use HtmlWebpackPlugin to load Webpack bundle in Django
If you want a quick start with Webpack and Django, please check python-webpack-boilerplate