The purpose of this repository is to demonstrate a slightly more canonical (but lesser known) approach of including Webpack bundles from a Vue CLI project into Django templates without any additional plugins. Instead, it makes use of:
- index.html generated by html-webpack-plugin (which is bundled with Vue CLI);
{% extends %}
tag in Django templates.
All of the Vue.js / Vue CLI / Webpack goodies — such as the dev server, client-side routing, hot module replacement, code-splitting, filename hashes, prefetch tags — should work fine in this demo. In the dev mode, the bundles are served from memory, not the disk.
The approach has been popularized by @Ejez. You can read up on it here, here and here. The official Vue CLI documentation also gives a hint about it:
you should consider using the indexPath option to use the generated HTML as a view template in your server-side framework
Why not just use django-webpack-loader?
Another and more common approach (described here and here) makes use of django-webpack-loader, a Django extension which consumes output of webpack-bundle-tracker (Webpack plugin from the same author, @owais) and provides {% render_bundle %}
template tag.
And it’s a fine approach! If it works well for you, there is no reason to switch. But it’s good to have options to choose from, right?
server/
— basic Django (3.0) project.client/
— basic Vue CLI app generated by Vue CLI (4.3).
It’s a minimal working demo that you can easily run and play around with, NOT a starter project or boilerplate.
Additionally, there are VS Code tasks for running dev servers under .vscode/
. If you don’t use VS Code, you don’t need this directory.
- Clone the repo and
cd
into the directory. - Install Django:
pip install django
- Install Vue.js project dependencies:
cd client && npm install
- Now you can:
- run the Vue.js dev server:
npm run serve
- build for production:
npm run build
- run the Vue.js dev server:
cd
to theserver/
directory and run Django dev server from it:python manage.py runserver
- Open
http://127.0.0.1:8000/
in your browser.
A recommended way to get a quick grasp of how it all works:
- See the changes introduced in commit 7a3df2a. The changes are minimal.
- Read @Ejez's short explanation.
- Run the project (see instructions above).
But if you prefer reading a long text instead, have fun.
Let’s start from the official documentation for Vue CLI:
The file public/index.html is a template that will be processed with html-webpack-plugin. During build, asset links will be injected automatically. In addition, Vue CLI also automatically injects resource hints (preload/prefetch), manifest/icon links (when PWA plugin is used), and the asset links for the JavaScript and CSS files produced during the build.
In this demo, we modified the client/public/index.html
template so that it is also a valid Django template that extends another Django template (server/templates/base.html
). That’s right: client/public/index.html
is a valid template for both Vue.js and Django, but Vue.js treats it like regular HTML, ignoring Django-specific tags, like {% extends %}
.
During build, Webpack of the Vue CLI app injects all necessary asset links into the template and saves the resulting file as base-vue.html
into the Django templates directory (server/templates/
) — as prescribed by indexPath
option in client/vue.config.js
:
// outputDir resolves to server/static/dist
outputDir: '../server/static/dist',
// indexPath is relative to outputDir and resolves to server/templates/base-vue.html
indexPath: '../../templates/base-vue.html',
In Django’s server/urls.py
we defined a TemplateView
to serve server/templates/index.html
— which is a template that extends base-vue.html
. (Therefore, if you run the Django dev server before building the Vue CLI project, you will get a TemplateDoesNotExist
error).
So the hierarchy of our Django templates can be depicted as:
base.html
└── base-vue.html <- generated by Webpack from client/public/index.html
└── index.html <- served by Django at /
In base-vue.html
we use the {{ block.super }} technique to preserve the contents of <head>
and <body>
tags from base.html
. In a similar fashion you can customize the contents of these tags in children templates.
Worth noting that by default the Vue.js dev server does not write any files to the disk, instead it serves everything from memory. However, we need base-vue.html
on the disk, because we configured Django to use it as a template. This can be easily achieved using the Webpack’s devServer.writeToDisk option. In client/vue.config.js
we defined an arrow function for devServer.writeToDisk
that tells Webpack to write only index.html
and keep everything else in memory:
chainWebpack: (config) => {
config.devServer.writeToDisk((filePath) => filePath.endsWith("index.html"));
};
Everything is extremely easy to set up for more than one template to be exported with Vue embedded!
Only few small tweaks are needed:
- create a
pages
folder and with several subfolders named after the pages you want. e.g.About
andIndex
- copy
App.vue
andmain.js
into those folders. Match the following:
client
|--src
|--pages
|--about
| |--App.vue
| |--main.js
|--index
|--App.vue
|--main.js
- change
vue.config.js
with the following changes: addpages
object, removeindexPath
and updatewriteToDisk
function.
module.exports = {
publicPath:
process.env.NODE_ENV === "production"
? "/static/dist/"
: "http://127.0.0.1:8080",
//Added pages per Vue Docs
pages: {
index: {
entry: "./src/pages/home/main.js", //matching the new paths created above
template: "public/index.html",
filename: "../../templates/base-vue.html", //relative to outputDir!
chunks: ["chunk-vendors", "index"],
},
about: {
entry: "./src/pages/about/main.js", //matching the new paths created above
template: "public/index.html",
filename: "../../templates/base-vue-about.html", //relative to outputDir!
chunks: ["chunk-vendors", "about"],
},
},
outputDir: "../server/static/dist",
chainWebpack: (config) => {
/*
The arrow function in writeToDisk(...) tells the dev server to write
only index.html to the disk.
The indexPath option (see above) instructs Webpack to also rename
index.html to base-vue.html and save it to Django templates folder.
We don't need other assets on the disk (CSS, JS...) - the dev server
can serve them from memory.
See also:
https://cli.vuejs.org/config/#indexpath
https://webpack.js.org/configuration/dev-server/#devserverwritetodisk-
*/
config.devServer
.public("http://127.0.0.1:8080")
.hotOnly(true)
.headers({ "Access-Control-Allow-Origin": "*" })
.writeToDisk((filePath) => filePath.endsWith(".html")); //NOTE THIS CHANGE HERE
},
};
Just open an issue. Please note that issues unrelated to the purpose of this repository will be marked as closed.