- 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 the end of this chapter, you should be able to:
- Create a
Webpack
project and make it run inwatch
mode. - Understand what is Webpack loader
- Learn how to use Webpack loaders to work with SCSS and modern Javascript
Django Project
Before we work on Webpack part, let's first create a Django project.
Create Project
$ mkdir django_webpack && cd django_webpack
$ python3 -m venv env
$ source env/bin/activate
You can also use other tool such as Poetry or Pipenv
Create requirements.txt
django
(env)$ pip install -r requirements.txt
(env)$ env/bin/django-admin.py startproject django_webpack_app .
We will see structure like this
.
├── django_webpack_app
├── env
├── manage.py
└── requirements.txt
Run DevServer
Let's run the Django application.
# migrate db.sqlite3
(env)$ ./manage.py migrate
# runserver
(env)$ ./manage.py runserver
System check identified no issues (0 silenced).
January 22, 2021 - 02:10:17
Django version 3.1.5, using settings 'django_webpack_app.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
- Now if we visit
http://127.0.0.1:8000/
we will see welcome page which hasThe install worked successfully! Congratulations!
. - We will see
db.sqlite3
is created at the project directory, here we did not specify Django to use other db so sqlite is used by default.
Webpack project
Please make sure you have node
and nvm
installed. If not, you can get it:
- On nodejs homepage
- Using nvm I recommend this way.
$ node -v
v12.20.0
$ npm -v
6.14.8
Basic Concepts
webpack is an open-source JavaScript module bundler. It is made primarily for JavaScript, but it can transform front-end assets such as HTML, CSS, and images if the corresponding loaders are included
Now Webpack
is the most popular bundle solution in frontend community, so we will learn how to config it and use it with Django project.
Setup
Considering webpack
is a little complicated to newbie developers, we can use some awesome boilerplate project on the Github to help us quickly get started and then dive deeper.
Here I recommend wbkd/webpack-starter, which have all the features we need and is actively maintained.
Let's clone the project to the frontend
directory.
$ git clone [email protected]:wbkd/webpack-starter.git frontend
.
├── db.sqlite3
├── django_webpack_app
├── frontend
│ ├── LICENSE
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ ├── src
│ │ ├── index.html
│ │ ├── scripts
│ │ │ └── index.js
│ │ └── styles
│ │ └── index.scss
│ └── webpack
│ ├── webpack.common.js
│ ├── webpack.config.dev.js
│ └── webpack.config.prod.js
├── manage.py
└── requirements.txt
Notes:
frontend/webpack
contains config file forwebpack
, the config files are split fordevelopment
andproduction
.frontend/src
contains the sourcejs
,scss
andindex.html
. I will talk about them in a bit.- Please note that there are some dot files (
.babelrc
,.eslintrc
,.stylelintrc.json
) in thefrontend
directory, they are required to make the Webpack project work.
Now let's run the frontend project
$ cd frontend
# delete .git directory in frontend directory
(frontend)$ rm -rf ./.git
# install dependency in frontend/node_modules
(frontend)$ npm install
# run start command
(frontend)$ npm run start
Project is running at http://localhost:8080/
If we visit http://localhost:8080/
we will see the successful message.
Press Ctrl-C
to terminate the npm run start
Start command
Now the frontend
app can run without error but we still need to update some config to make it work with our Django project.
Update scripts.start
in wagtail_project/frontend/package.json
{
"scripts": {
"start": "webpack --watch --config webpack/webpack.config.dev.js"
},
}
Notes:
- We changed the
start
command towebpack --watch --config webpack/webpack.config.dev.js
--watch
means we runwebpack
inmode
mode (webpack can watch files and recompile whenever they change)
Why we do that?
- The default start command is
webpack serve --config webpack/webpack.config.dev.js
, which would launchwebpack-dev-server
on local. webpack-dev-server
doesn't write any output files after compiling. Instead, it keeps bundle files in memory and serves them as if they were real files mounted at the server's root path- This makes
webpack-dev-server
not work with Django in easy way, so here we choose to usewatch
mode which is easy to understand. - I will talk about how to use
webpack-dev-server
with Django in later chapter.
$ cd frontend
# run start command
(frontend)$ npm run start
[webpack-cli] watching files for updates...
Congratulations! Now the webpack can work in watch
mode.
Press Ctrl-C
to terminate the npm run start
We can see something like this in the frontend/build
./build
├── index.html
├── js
│ ├── app.js
│ └── vendors-node_modules_css-loader_dist_runtime_api_js-node_modules_css-loader_dist_runtime_cssW-926fd9.js
└── public
Next, let's check the webpack config to understand how it works.
Webpack Entry and Output
Let's check webpack/webpack.common.js
module.exports = {
entry: {
app: Path.resolve(__dirname, '../src/scripts/index.js'),
},
output: {
path: Path.join(__dirname, '../build'),
filename: 'js/[name].js',
},
}
Notes:
- The
entry
define the entry of the frontend app. - The
output
define the build directory and the bundle filename. We will learn more about this in later chapter.
Webpack loader For SCSS
What is Webpack loader?
Loaders are transformations that are applied to the source code of a module. They allow you to pre-process files as you import or “load” them.
Loaders can be chained. Each loader in the chain applies transformations to the processed resource. A chain is executed in reverse order.
Let's check module.rules
in frontend/webpack/webpack.config.dev.js
{
test: /\.s?css$/i,
use: ['style-loader', 'css-loader?sourceMap=true', 'postcss-loader', 'sass-loader'],
},
Notes:
- The
test
is a JS regex and it would matchscss
andcss
filenames - As we know, the loaders can be a chain which is executed in reverse order
- So the loaders would work like
style-loader(css-loader?sourceMap=true(postcss-loader(sass-loader(file.scss))))
Next, let's update frontend/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',
},
devServer: {
inline: true,
hot: true,
},
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'],
},
],
},
});
- We import
MiniCssExtractPlugin
at the top new MiniCssExtractPlugin({filename: 'css/app.css',})
in theplugins
tell Webpack to put all css code into one single fileapp.css
- In the rule for css/scss file, we removed the first
style-loader
, which inject css into the dom because we do not need it in this project. MiniCssExtractPlugin.loader
would make sure all css code would be put intoapp.css
.
So here is the workflow:
sass-loader
compilescss
tocss
postcss-loader
would parse css and add vendor prefixes to CSS rules.css-loader
interprets@import
andurl()
in css.MiniCssExtractPlugin.loader
extract css code toapp.css
As for the sourcemap
,
- We changed the
devtool
toinline-cheap-source-map
for better SCSS debug. (This is optional)
Now, let's rerun the npm run start
, and we can see files like this
./build
├── css
│ └── app.css
├── index.html
├── js
│ └── app.js
└── public
Notes:
css/app.css
is created.- If we check
app.css
, we can see code/*# sourceMappingURL=data:application/json;
at the bottom of the file, which issoucemap
.
Webpack loader for modern Javascript
Let's check frontend/webpack/webpack.config.dev.js again!
module: {
rules: [
{
test: /\.js$/,
include: Path.resolve(__dirname, '../src'),
loader: 'babel-loader',
},
],
},
Notes:
babel-loader
is to makeBabel
to work with Webpack.
Babel is a free and open-source JavaScript transcompiler that is mainly used to convert ECMAScript 2015+ (ES6+) code into a backwards compatible version of JavaScript that can be run by older JavaScript engines.
With babel-loader
, we can write JS code in modern syntax and the code can still work on old browser.
The babel
config is in frontend/.babelrc, you can check babeljs doc to learn more.
Conclusion
- 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