A Vue.js workflow for Django
Pairing JavaScript tooling like Vue CLI and create-react-app with traditional frameworks like Django is notoriously hard.
There isn't a "right way" to do this stuff, but thanks to Vue.js configurability you can at least choose where to put the resulting bundle.
In the following notes I present a Vue.js workflow for Django which I found out working well for most use cases.
Book in progress!
Stay tuned on my upcoming book for Apress "Decoupled Django". Understand and build decoupled Django architectures for JavaScript frontends. Follow on Twitter and Telegram for news and sneak peaks.
Configuring Vue.js
Suppose you have a Django app named billing
, and you want to make the frontend of this app a single-page.
You also want to serve this single-page from within Django's umbrella to use Django built-in authentication.
First off, we generate a Vue project inside the app with Vue CLI, let's say in repo-root/billing/billing_spa
Then, we set up vue.config.js
, in the same Vue project folder, with the following configuration:
const path = require("path");
module.exports = {
publicPath: process.env.VUE_APP_STATIC_URL,
outputDir: path.resolve(__dirname, "../static", "billing"),
indexPath: path.resolve(__dirname, "../templates/", "billing", "index.html"),
};
With this configuration we say to Vue:
- put static assets inside
billing/static/billing
- put the
index.html
insidebilling/templates/billing
Django is highly configurable in regard to static files and template structure, but this setup respects Django expectations on where to find static files and templates. Your mileage may vary.
Next up, in the Vue project folder billing/billing_spa
, we create an environment file named .env.staging
with the following content:
VUE_APP_STATIC_URL=/static/billing/
For the setup to work, Django's STATIC_URL
in development should match STATIC_URL=/static/
.
For production, we create another environment file named .env.production
with the following content:
VUE_APP_STATIC_URL=https://static.your-domain.com/billing/
With this configuration we move to setting up the view.
Serving Vue.js under a TemplateView
To serve the single-page, we create a view in billing/views.py
as a subclass of TemplateView
:
from django.views.generic import TemplateView
class Index(TemplateView):
template_name = "billing/index.html"
We then proceed to wire up the url configuration as usual (not shown here for brevity).
Once everything is in place, we can run the project in development, and in production.
How about the base template?
If you have a base template let's say in project_name/templates/base.html
with some navigation, you can extend it in billing/billing_spa/public/index.html
:
{% extends "base.html" %}
{% block someblock %}
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
{% endblock %}
Serving the single-page with Django in development
In development, you run the Vue.js build as follows:
npm run build -- --mode staging
This will place all the files in the appropriate folders. Once done you can run Django development server and have your app served:
python manage.py runserver
With this setup you lose hot reloading and instant changes, but you're always free to npm run serve
from Vue CLI to work in instant mode before building the project.
I consider this setup a "pre-staging" environment where I can test how Vue works within Django, with the built-in authentication system for example.
Note that I've tested this setup only with the Vue router in hash mode, though it shouldn't be hard to make it work in history mode too.
Serving the single-page with Django in production
In production, you run the Vue.js build as follows:
npm run build -- --mode production
This will place all the files in the appropriate folders again, the only difference being that publicPath
will now point to the complete domain.
It's your turn now to deploy the code on your production machine, run python manage.py collectstatic
and have the reverse proxy serve the static folder.
Would love to know what's your approach, feel free to reach me out on Twitter @gagliardi_vale.
Thanks for reading!