/django-vue-cli-webpack-demo

A minimal demo showing how to wire up Webpack of a Vue CLI app to Django templates without extra plugins.

Primary LanguagePython

Django + Vue CLI + Webpack demo

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?

What’s inside the repository?

  • 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.

Running

  1. Clone the repo and cd into the directory.
  2. Install Django: pip install django
  3. Install Vue.js project dependencies: cd client && npm install
  4. Now you can:
    • run the Vue.js dev server: npm run serve
    • build for production: npm run build
  5. cd to the server/ directory and run Django dev server from it: python manage.py runserver
  6. Open http://127.0.0.1:8000/ in your browser.

How does this work, exactly?

A recommended way to get a quick grasp of how it all works:

  1. See the changes introduced in commit 7a3df2a. The changes are minimal.
  2. Read @Ejez's short explanation.
  3. 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"));
};

Suggestions, questions?

Just open an issue. Please note that issues unrelated to the purpose of this repository will be marked as closed.