Django + webpack + Vue.js - setting up a new project that's easy to develop and deploy (part 1)
UPDATE 2018-11-12: Rodrigo Smaniotto wrote an updated version and more complete than this article using Dajngo2 and Vue CLI3. I can only recommend checking it here: https://medium.com/@rodrigosmaniotto/integrating-django-and-vuejs-with-vue-cli-3-and-webpack-loader-145c3b98501a
UPDATE: I recently created a working project at ariera/django-vue-template using the latest version of the vue-webpack template and bundling all the changes highlighted in this post in just this commit 8a5c552. You may also be interested in checking a vue project template that CharlesAracil created taking inspiration from this entry.
I recently started a new web project based on both Django and Vuejs. It took a good deal of reading half-related and/or outdated posts, tons of documentation, and a good deal of trial and error to get it right.
In this post we will cover the key points on setting up a nice and solid development environment that is consisten with production and hence easy to deploy. The specifics of how to deploy, final configuration touches and (automation) will be discussed in a future article.
This is the solution that I found, there may be better alternatives (which I’d be happy to read) and it is utimately written with the intention of helping others as well as my future-self :)
The problem
Both Django and Vue offer local web servers to facilitate development, but that confused me in the beginning. Does that mean that I need to be running both servers in parallel? And do I need to switch from one to another while developing? That sounds cumbersome! If you are building a full SPA with Vue as your frontend, and Django as a simple API backend that might be okey, but I want something else.
I want to remain flexible: some parts of my project will behave like an SPA, other parts will be rendered by Django, and some other will be a mixture: Django-rendered with Vue components. I want to be able to use my webpack-compiled css in my Django templates, as well as other static assets.
My goals
- minimise differences in between prod & dev
- use just one url in development (ie Django’s runserver should proxy to webpack’s dev server, or the other way around)
- preserve hot reload (ie. HMR) in development
- django backed pages should work without much ceremony
My solution
The solution I found is based around the django library django-webpack-loader
and the webpack-bundle-tracker
webpack plugin. The django module will read a manifest file, and a webpack plugin take care of updating it. That way, webpack will compile all the assets and Django will be able to use them in your templates.
All you will need to do while developing is launch both local web servers and point your browser to the Django one :)
Versions i’m using:
- Vue.js 2.4.2
- Django 1.11
- vue-cli 2.8.2
- Python 3.6.1
Setting up the dev environment
Creating the project
I have used the vue-cli to create a new project based on the webpack template, it will do most of the configuration and setup for us. Then, inside the new directory holding my project, I had created a new django project:
Now install both libraries
Add django-webpack-loader
to your INSTALLED_APPS
, we will get to the specific configuration in a minute.
And configure webpack to use BundleTracker
.
Defining the statics paths
This part was one of the trickiest, there are several configuration options all with very similar names and they feel very confusing to new commers to the framework. I’ll spend a little explaining what and why every option does.
My goal here was to have in the end a directory named ./public
(at the root of the project) where all the assest will be stored, with no unnecesary subdirectories (ie. grouping all javascript inside a js
dir would be okey, but theres no need for deeper levels). The final ./public
folder would look like this
- public/
- js/ (webpack generated)
- app.c611f8fd9b27da4ec95f.js
- app.c611f8fd9b27da4ec95f.js.map
- ...
- css/ (webpack generated)
- ...
- img/ (django generated)
- ...
- admin/ (django generated)
- ...
In order for this to happen we have to configure both worlds properly. Let’s take a look at the final configuration of both Django and webpack, and then we will discuss them.
Let’s analyse this step by step.
-
build.assetsRoot: path.resolve(__dirname, '../dist/')
This is the output directory of our
npm run build
task, that takes care of compiling and building all of our webpack-controlled assets. We will run this task as part of the deployment process. It will take care of analysing all our webpack entry points, traversing and compiling them. The out put will be stored in the./dist
directory at the root of our project (which should be gitignored). -
STATICFILES_DIRS
andSTATIC_ROOT
When deploying to production we will also run the Django script
collectstatic
that takes care of finding all the static files our project makes use of, and putting them in the correct folder. WithSTATICFILES_DIRS
we are telling it were to look for these files. WithSTATIC_ROOT
we specify where to place them.In
STATIC_DIRS
we have specified 2 directories: one is the./static
(Django’s default), and the above defined./dist
where webpack will place its compiled assets. -
build.assetsSubDirectory: ''
andbuild.assetsPublicPath: '/static/'
Now we tell webpack that (in production) any asset reference should point to
/static
(ie at the root of the domain). For example, if our code reads:<img src="images/logo.png">
the final compiled version will be:
<img src="/static/images/logo.png">
-
STATIC_URL = '/static/'
The same thing but for Django. Everytime one of our templates referes to a static file it will render it under the
/static
path. -
dev.assetsPublicPath: 'http://localhost:8080/'
This is one of the key points in which we achieve our goal number 2 (use only one url in dev). We want the webpack dev server to serve our webpack assets. While we point our browser to Django’s
localhost:8000
, webpack will compile our code to point atlocalhost:8080
(notice those are 2 different ports). -
WEBPACK_LOADER
In
WEBPACK_LOADER['DEFAULT']['STATS_FILE']
we point to the json manifest file generated by webpack’sBundleTracker
(see above in filebuild/webpack.base.conf.js
).And we set to blank
WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME']
because we don’t want to nest our webpack assets into any subdirectory.
Referencing static files from both Vue and Django
One final touch. I would like to have a place to put images, fonts, or generic statics assets that can be used from both worlds: Django tempaltes and Vue components. In other words, I want to have a ./static
directory at the root of the project where I could put (for example) my logo and be able to write this code:
The Django example already works thanks to the config above described (see STATICFILES_DIRS
), but for Vue/Webpack to work you’d need to add an alias to the wbepack config, like this.
Thanks to this we can now write our Vue code like this (mind the ~
):
Hot realod or Hot Module Replacement (HMR)
While in development we are only accessing Django’s server, and proxying our asset requests to the webpack server (see above dev.assetsPublicPath
). Because of this HMR or hot reload is broken. We need to configure webpack’s hot middleware to point to the right path, and we need to add a new header to the webpack dev server so it allows for CORS requests.
The official vue webpack template was updated around 2 weeks ago to start using the official webpack-dev-server
.
In order to properly configure HMR after the update you simply need to add the following to the build/webpack.dev.conf.js
:
Thank you very much to Emilien and LinusBorg for the feedback on this :)
The basic Django template
We will define a Django template reusable by other parts of our application that will take care of including the webpack assets.
Now other parts of our project could just extend this template and benefit from having all the styles from css or javascript accessible. In fact, let’s create a second template, extending this one, to serve as the root of our SPA:
Notice that in order for this template to be detected by Django you need to add the following to your settings.py
And in our main.js
where we define the root component of our SPA, we will point to this div#app
:
The last step would be to define a new url route in our Django settings:
And we can now open http://localhost:8000
in our browser and enjoy our fully functional dev environment :)
Clean unnecessary files and options
UPDATE: It is better ignore everything related to HtmlWebpackPlugin
in this part, it was breakign the end-to-end tests.
Before we conclude let’s do a little cleanup.
Remove the index.html
file at the root of your project and stop using HtmlWebpackPlugin
in development. Since Django will render the initial html and include any assets there we don’t need HtmlWebpackPlugin
to generate an initial html file for us.
For the same reason we want to remove it from the production config, but we want to keep it in tests. Simply move this bit of conde from build/webpack.prod.conf.js
to build/webpack.test.conf.js
And last but not least lets add the following to your .gitignore
file:
Conclusions:
To have a good dev environment we have used Django as our main dev server, which will in turn proxy any asset request to webpack. This required a good deal of configuration. In doing so we have simplified the development workflow so we can spend more time programming and less time switching between browsers or fine tuning the config for new cases that may appear.
We have also paved the path to production deployment, but that’s something that we will cover in the next post.
Until then,
Happy hacking!
References used
- https://stackoverflow.com/questions/41342144/webpack-hmr-webpack-hmr-404-not-found
- https://github.com/webpack/webpack-dev-middleware
- https://gist.github.com/Belgabor/130e7770575e74581b67597fcb61717e
- https://github.com/ezhome/django-webpack-loader/tree/master/examples
- https://github.com/rokups/hello-vue-django
- https://gist.github.com/genomics-geek/81c6880ca862d99574c6f84dec81acb0
- https://cscheng.info/2016/08/03/integrating-webpack-dev-server-with-django.html
- http://vuejs-templates.github.io/webpack/backend.html
- http://owaislone.org/blog/webpack-plus-reactjs-and-django/