- 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
Webpackproject and make it run inwatchmode. - 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.sqlite3is 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/webpackcontains config file forwebpack, the config files are split fordevelopmentandproduction.frontend/srccontains the sourcejs,scssandindex.html. I will talk about them in a bit.- Please note that there are some dot files (
.babelrc,.eslintrc,.stylelintrc.json) in thefrontenddirectory, 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
startcommand towebpack --watch --config webpack/webpack.config.dev.js --watchmeans we runwebpackinmodemode (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-serveron local. webpack-dev-serverdoesn'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-servernot work with Django in easy way, so here we choose to usewatchmode which is easy to understand. - I will talk about how to use
webpack-dev-serverwith 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
entrydefine the entry of the frontend app. - The
outputdefine 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
testis a JS regex and it would matchscssandcssfilenames - 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
MiniCssExtractPluginat the top new MiniCssExtractPlugin({filename: 'css/app.css',})in thepluginstell 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.loaderwould make sure all css code would be put intoapp.css.
So here is the workflow:
sass-loadercompilescsstocsspostcss-loaderwould parse css and add vendor prefixes to CSS rules.css-loaderinterprets@importandurl()in css.MiniCssExtractPlugin.loaderextract css code toapp.css
As for the sourcemap,
- We changed the
devtooltoinline-cheap-source-mapfor 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.cssis 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-loaderis to makeBabelto 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