Skip to content

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

Notifications You must be signed in to change notification settings

ihelmer07/django-vue-cli-webpack-demo

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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"));
};

Want to do use it for more than one template (Multi Page App or MPA)?

Everything is extremely easy to set up for more than one template to be exported with Vue embedded!

Only few small tweaks are needed:

1. Set up the Vue project to be a MPA:

  1. create a pages folder and with several subfolders named after the pages you want. e.g. About and Index
  2. copy App.vue and main.js into those folders. Match the following:
client
|--src
   |--pages
      |--about
      |   |--App.vue
      |   |--main.js
      |--index
          |--App.vue
          |--main.js
  1. change vue.config.js with the following changes: add pages object, remove indexPath and update writeToDisk 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
  },
};

Suggestions, questions?

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

About

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

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 46.9%
  • Vue 24.2%
  • JavaScript 16.1%
  • HTML 12.8%