0👍
Here is a first solution approach for django + Vue.js v2. It includes some stuff that is only relevant for django CMS. But the general principle applies to any server-side rendering CMS or web framework.
Enable Vue.js support in your Webpack config, something like the below:
frontend/webpack.config.js
:
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require(`mini-css-extract-plugin`);
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const isServerMode = process.env.NODE_ENV === 'production';
const config = {
mode: 'development',
entry: {
// here, any number of entrypoints (bundles) can be defined
"marketplace-app": './frontend/blocks/marketplace-app/index.js',
},
output: {
filename: '[name].bundle.js',
path: __dirname + '/dist/',
publicPath: `http://localhost:8090/assets/`,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.svg$/i,
exclude: /fonts/,
loader: 'svg-url-loader',
},
{
test: /\.(sass|scss|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
sourceMap: true,
plugins: () => {
return [
require('precss'),
require('autoprefixer'),
];
},
hmr: true,
}
},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'sass-loader', options: {sourceMap: true}},
]
},
{
// images
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
query: {
hash: 'sha512',
digest: 'hex',
name: '[name].[ext]'
}
}
},
{
loader: 'image-webpack-loader',
options: {
query: {
bypassOnDebug: 'true',
mozjpeg: {progressive: true},
gifsicle: {interlaced: true},
optipng: {optimizationLevel: 7},
}
}
}
]
},
{
test: /\.(svg)(\?[\s\S]+)?$/,
// svg fonts cannot be processed the way we do with svg images above
// therefore they are handled separately here
include: /fonts/,
use: [
'file-loader'
]
},
{
test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
},
{
test: /\.(ttf|eot)(\?[\s\S]+)?$/,
loader: 'file-loader',
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader'],
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader'],
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader'
}, /*{
loader: 'eslint-loader' // You can uncomment this if you want compiling to fail if linting fails
}*/]
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.vue'],
modules: [
path.resolve('frontend'),
'node_modules'
],
alias: {
modernizr$: path.resolve(__dirname, '/frontend/.modernizrrc'),
vue: process.env.NODE_ENV === 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
}
},
devServer: {
contentBase: path.resolve(__dirname, `frontend`),
headers: {'Access-Control-Allow-Origin': '*'},
host: `localhost`,
port: 8090,
hot: true,
inline: true,
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: '[name].css'}),
],
devtool: 'eval-source-map',
optimization: {
// the default config from webpack docs, most of it might be useless
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
if (isServerMode) {
config.mode = 'production';
config.devtool = 'none';
config.output.filename = '[name].js';
config.output.publicPath = '/static/dist/';
}
module.exports = config;
example for the server-side template (i.e. in django this could be something like backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html
):
Notes:
- This demos how to import one value (an endpoint url) from the django template into the Vue.js application.
- supports local dev env mode with hot reloading via webpack dev server based on the value of a global setting variable
settings.DJANGO_ENV
settings.WEBPACK_DEV_URL
needs to be set topublicPath
from the Webpack dev server setting inwebpack.config.js
data-is-reload-on-page-edit
class needs to be tied to CMS functionality that reloads the js sources upon a frontend editing action, if CMS has such functionality.
{% load i18n staticfiles static thumbnail %}
{% comment %} this is a vue widget from frontend/blocks/marketplace-app/index.js {% endcomment %}
<div id="{{ instance.anchor_id }}">
<div class="marketplace-vue-widget" data-endpoint-url='{% url 'marketplace:article-list' %}'></div>
</div>
{% if settings.DJANGO_ENV.value == 'local' %}
<script data-is-reload-on-page-edit defer src="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.bundle.js"></script>
<link rel="stylesheet" href="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.css">
{% else %}
<script data-is-reload-on-page-edit defer src="{% static 'dist/marketplace-app.js' %}"></script>
<link rel="stylesheet" href="{% static 'dist/marketplace-app.css' %}">
{% endif %}
Frontend setup (frontend/blocks/marketplace-app/index.js
):
import Vue from 'vue';
import App from './app.vue'
// this relates to backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html
// CMS plugins could (in theory) be added more than once to a page,
// we take care to allow the vue instance to be mounted on any occurrences
// create a constructor for your widget
let Widget = Vue.extend({
render(h) {
return h(App, {
props: { // load stuff from data attributes in the django template
endpointUrl: this.$el.getAttribute('data-endpoint-url'),
}
})
},
});
// mount the widget to any occurences
let nodes = document.querySelectorAll('.marketplace-vue-widget'); // unique selector!
for (let i = 0; i < nodes.length; ++i) {
new Widget({el: nodes[i]})
}
app.vue
:
<template src='./app.html'></template>
<script src="./app.js"></script>
<style lang="scss" scoped src="./app.scss"></style>
app.js
:
// example code
export default {
props:["endpointUrl"],
data() {
return {}
},
computed: {},
mounted() {},
watch: {},
methods: {},
components: {},
}
Source:stackexchange.com