我曾经在寻找实现与提问者类似的功能时看过这个问题和其他一些问题。不幸的是,关于Vue的大部分信息都是在单页应用程序(SPA)的上下文中给出的。然而,正如Evan You经常重复的那样,Vue没有偏见,也不需要在SPA内使用。
我想分享一些我的发现,并勾勒出一种可能的方法来使Django和Vue协同工作。虽然不需要SPA,但我认为Vue的真正力量来自于它的组件,这有点推动你朝着Webpack或类似的方法,而不是简单的独立模式在HTML中。
此外,只要非常清楚:90%以上的我的Django视图代码和模板仍然与以前完全相同。 Django并不特别关心它是否使用webpack_loader和render_bundle。甚至更少的是,render_bundle与Vue有关。通过Django的“template blocks”,“verbatim”标签和Vue的“slots”,您可以获得很多灵活性,允许您保留大部分现有内容。
最后,一旦您的应用程序稳定,您可以在端口3000上禁用热重新加载服务器,运行npm run build-production
并使用collectstatic
将您的JS通过nginx / apache像任何普通静态内容一样提供。所以node
作为偶尔的批处理而不是服务运行。需要对Django的配置进行一些调整,但完全可以接受。
抱歉,这很草图,不要期望工作代码,因为我正在剥离很多东西。但希望它能给你一个想法。
mysite / __full12_vue.html:
这是我的基本Vue-ify Django模板,它扩展了我的现有Django基本模板__full12.html。
假设__full12.html定义了所有一般的Django块,如{% block content %}等
(实际上,有一个非常重要的带有ID bme-vue
的div,所以我也将此模板添加到了最后。)
我添加了一个Vue组件来显示用户消息。
并重新定义了菜单模板,以使用Vue + Bootstrap下拉菜单。
{% extends "mysite/__full12.html" %}
{% load render_bundle from webpack_loader %}
{% block nav %}
{% include "mysite/menu_vue.html" %}
{% endblock nav %}
{% block user_msg %}
<div class="row">
<div class="col-6">
<bme-user-messages>
<div>THIS SHOULDNT APPEAR ON SCREEN IF VUE WORKED</div>
</bme-user-messages>
</div>
</div>
{% endblock user_msg %}
{%block extra_js_body_end%}
{% render_bundle bundle_name %}
{%endblock extra_js_body_end%}
webpack.config.development.js:
这是您告诉Webpack要为视图中指定的bundle_name提供哪些JS文件的地方。
如何配置Webpack超出了我的帖子范围,但我可以向您保证,这确实是一件真正令人头痛的事情。我最初使用pip django-webpack-loader,然后尝试https://github.com/NdagiStanley/vue-django和Bootstrap 4等内容。然而,最终结果比单独使用要强大得多,这是我个人的看法。
/*
webpack config dev for retest
*/
config.entry = {
"main" : [
'webpack-dev-server/client?http://localhost:3000','../../static_src/js/index'],
// ....stuff..
//KEY: ONE entrypoint for EACH bundlename that you use.
"mydjangoappname/some_django_view" : ["../../static_src/js/mydjangoappname/some_django_view"],
"mydjangoappname/another_django_view" : ["../../static_src/js/mydjangoappname/another_django_view"],
// ....stuff..
}
// ....stuff left out...
mydjangoappname/some_django_template.html
最后,我们准备展示一些实际内容:
bme-nav-item
和
bme-tab-pane
是我用于 Boostrap 4 标签导航和内容的 2 个自定义 Vue 组件。
Django 使用
var settings= some-json-object
将实例特定数据(而不是页面通用数据)传递给 Vue 和 JS。
{% extends "mysite/__full12_vue.html" %}
<script>
var settings = {{settings | safe}};
</script>
{% block content %}
<button v-bind:data-url="url_batch" type="button" class="btn btn-block btn-outline-primary" @click.prevent="run_batch">
<ul id="tab-contents-nav" class="nav nav-tabs nav-pills">
{% if templatename_details %}
<bme-nav-item link="details"
label="{{details_label}}" >
</bme-nav-item>
{% endif %}
<bme-tab-pane link="details">
<div slot="content">
{% if templatename_details %}
{% include templatename_details %}
{% else %}
<span class="text-warning">SHOULDNT APPEAR IF VUE WORKED </span>
{% endif %}
</div>
</bme-tab-pane>
{% endblock content %}
mydjangoappname/some_django_view.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import {base_messages, base_components} from '../mysite/commonbase.js';
var local_components = {
};
const components = Object.assign({}, base_components, local_components);
export function dovue(config) {
store.commit('initialize', config);
var li_tohide = settings.li_tohide || [];
li_tohide.forEach(function(hidden) {
store.commit('add_hidden', hidden);
});
var vm = new Vue({
el: '#bme-vue'
,components: components
,data: {
li_rowcount: window.settings.li_rowcount || []
,csrf_token: window.csrf_token
,url_batch: "some url"
}
,mounted: function () {
console.log("data.js.lifecycle.mounted");
}
,methods : {
,run_batch: function(e) {
console.assert(this.$data, COMPONENTNAME + ".run_batch.this.$data missing. check object types");
var url = e.target.dataset.url
post_url_message(this, url, this.csrf_token);
}
}
,store: store
});
var li_user_message = config.li_user_message || [];
li_user_message.forEach(function(user_message, i) {
vm.$emit(base_messages.EV_USER_MESSAGE, user_message);
});
return vm;
}
import {app_config, LOCALNAV_LINK, TOPNAV_LINK_OTHERS} from "./constants";
var page_config = {
localnav_link : LOCALNAV_LINK.data
, topnav_link: TOPNAV_LINK_OTHERS.system_edit_currentdb
};
var generated_config = window.settings;
var local_config = Object.assign({}, app_config, page_config, generated_config);
var vm = dovue(local_config);
vuex/generic.js:
这是一个天真的、大多数情况下只读的存储实现:
const state = {
active_tab: ""
,topnav_link: ""
,localnav_link: ""
,li_user_message: []
,s_visible_tabid: new Set()
,urls: {}
};
const mutations = {
initialize(state, config){
Object.assign(state, config);
},
set_active_tab(state, tabid){
if (! state.s_visible_tab.has(tabid)){
return;
}
state.active_tab = tabid;
},
};
export {state as generic_state, mutations};
为了让您了解一般的文件层次结构:
.
./manage.py
./package.json //keep this under version control
./
├── mydjangoappname
│ ├── migrations
│ └── static
│ └── mydjangoappname
├── node_modules
├ //lots of JavaScript packages here, deposited/managed via npm && package.json
├── static
│ └── js
├── static_src
│ ├── assets
│ ├── bundles
│ │ // this is where django-webpack-loader's default config deposits generated bundles...
│ │ // probably belonged somewhere else than under static_src ...
│ │ ├── mydjangoappname
│ ├── components
│ │ ├── mydjangoappname
│ ├── css
│ ├── js
│ │ ├── mydjangoappname
│ │ └── mysite
│ └── vuex
│ ├── mydjangoappname
├── staticfiles
│ // for Production, collectstatic should grab django-webpack-loader's bundles, I think...
├── templates
│ ├── admin
│ │ └── pssystem
│ ├── mydjangoappname
│ └── mysite
└── mysite
├── config
├ // where you configure webpack and the entry points.
├ webpack.config.development.js
├── sql
│ └── sysdata
├── static
│ └── mysite
└── templatetags
好的,我确实需要修改网站的基本模板,以确保div#bme-vue始终可用。
可能需要在这个模板和mysite/__full12_vue.html之间进行一些重构。
mysite/__full12.html:
<body>
<div id="bme-vue">
...more blocks...
{% block search %}
{% endblock search %}
<div id="main" role="main">
<div>
{% block content %}
{% endblock %}
</div>
</div>
...more blocks...
</div>
{%block extra_js_body_end%}
{%endblock extra_js_body_end%}
</body>
</html>