Usando Vue con django

Recientemente comencé en algún website de networkinges sociales usando Django. Estoy usando el motor de templates de django pnetworkingeterminado para llenar mis páginas. Pero en este momento, quiero agregar javascript para hacer que el sitio sea más dynamic. Esto significa:

  • El encabezado y el pie de página son los mismos en cada página . El encabezado debe tener un menu desplegable, un formulario de búsqueda que busca cuando está escribiendo.
  • Mi aplicación django actual tiene una plantilla base que tiene el encabezado y el pie de página HTML, ya que cada página debe tener esto.
  • El sitio consta de varias páginas , piense en la página de índice, página de perfil, página de logging. Cada una de estas páginas tiene algunos componentes dynamics comunes pero también muchos diferentes. Por ejemplo, la página de logging debe tener la validation de formulario sobre la marcha, pero la página de perfil no necesita esto. La página de perfil debe tener un feed de estado con desplazamiento infinito.

Quiero usar Vue para tratar con los componentes dynamics, pero no sé cómo debo comenzar. La aplicación no debe ser un SPA.

  • ¿Cómo debería estructurar el código Vue?
  • ¿Cómo debo agrupar esto? ¿Usando Gulp? ¿O tal vez django-webpack-loader ?
  • Todavía debería ser capaz de usar las tags de plantilla de Django, por ejemplo, quiero poder usar {% url 'index' %} en el menu desplegable.

2 Solutions collect form web for “Usando Vue con django”

Esto parece una pregunta basada en opinión para la cual no hay una respuesta clara.

Mencionó que no desea que la aplicación sea una aplicación de una sola página (SPA) . Si es así, ¿cuál es la motivación para usar Vue? Para manejar las interacciones del usuario dentro de la página?

Vue puede funcionar perfectamente bien en un context no SPA. Le ayudará a manejar interacciones ricas dentro de la página, como vincular sus datos a menus desplegables, formularios, etc. Pero el poder real de Vue aparece cuando lo usa en el context de SPA.

Para su caso, recomendaría usar Vue.js en modo independiente , donde puede definir rápidamente la template dentro de los componentes Vue y escribir todo su código fácilmente en un file javascript.

Esto es lo que necesita: https://vuejs.org/guide/installation.html#Standalone

En el "modo independiente Vue.js", no hay necesidad de ningún sistema de compilation de webpack o vue-cli . Puede build la aplicación directamente en su entorno de desarrollo existente para django. gulp puede opcionalmente minimizar y agrupar tus files javascript normalmente, al igual que lo haces con tus aplicaciones basadas en jQuery.

Vue.js utiliza llaves dobles {{..}} para las templates, por lo que no interferirá con las cadenas de plantilla de django.

Todos los ejemplos de jsFiddle para Vue.js se ejecutan en modo independiente . Eso es precisamente lo que necesitas en este momento. Puede ver algunas de las preguntas recientes con la label vue.js , encontrar una muestra jsFiddle y ver cómo se hace.

Para aplicaciones SPA complejas, necesita build su código Vue por separado del lado del server, probarlo a background con llamadas ficticias AJAX, comstackrlo para la producción y luego colocar la versión final de producción en su server para realizar testings de extremo a extremo. Esto es algo que puedes hacer en el futuro.

Miré esta pregunta y otras hace un time mientras buscaba hacer prácticamente lo que el OP estaba pidiendo. Lamentablemente, la mayor parte de la información sobre Vue se proporciona en un context SPA. Sin embargo, como Evan You ha repetido a menudo, Vue no es obstinado y no requiere el uso dentro de un SPA.

Me gustaría compartir algunos de mis hallazgos y esbozar un posible enfoque para hacer que Django y Vue trabajen juntos. Si bien no se requiere un SPA, creo que el poder real de Vue proviene de sus Componentes y eso lo empuja hacia un Webpack o un enfoque similar, no un simple modo independiente en el html .

Además, solo para ser muy claro: el 90% del contenido del código de vista de Django y las templates se mantuvieron exactamente igual que antes. A Django no le importa particularmente que esté usando webpack_loader y render_bundle. Y aún less que render_bundle tiene algo que ver con Vue. Entre los template blocks Django, verbatim tags verbatim y las slots Vue, obtienes mucha flexibilidad que te permite dejar la mayor parte del contenido existente solo.

Por último, mi comprensión de django-webpack es que, cuando esté listo, puede collectstatic estadísticos y todos los packages generados por webpack pueden ser servidos por django / nginx / apache, como otros resources estáticos, sin ningún nodo o package web. De esta forma, no agregará partes mobilees complejas js a sus sistemas de producción. (nota: esto último es una suposition, por favor confirme o desmienta en los comentarios si sabe)

Disculpas, esto es bastante incompleto, no esperes código de trabajo ya que estoy despojando tanto. Pero con suerte te dará una idea.

mysite / __ full12_vue.html:

Esta es mi plantilla Vue-ify Django de base que amplía mi plantilla existente de Django, __full12.html .

  • supongamos que __full12.html define todos los bloques generales de Django, como {% block content%} y similares

    (en realidad, hay un div muy importante con ID bme-vue así que también he agregado esta plantilla al final).

  • He agregado un Componente Vue para mostrar los posts del usuario.

  • Y networkingefinió la plantilla de menu para usar los menus desplegables de Vue + Bootstrap.

 {% extends "mysite/__full12.html" %} <!-- KEY: use this to hook up to https://github.com/ezhome/django-webpack-loader --> {% load render_bundle from webpack_loader %} {% block nav %} <!-- uses Vue to setup Bootstrap dropdown menus --> {% include "mysite/menu_vue.html" %} {% endblock nav %} {% block user_msg %} <div class="row"> <div class="col-6"> <!-- uses Vue to display user messages --> <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%} <!-- KEY this points Django Webpack loader to appropriate Webpack entry point --> {% render_bundle bundle_name %} {%endblock extra_js_body_end%} 

webpack.config.development.js :

Aquí es donde le dice a Webpack qué JS debe servir para el nombre_compartido que especifica en la vista.

La configuration de Webpack está fuera del scope de mi publicación, pero puedo asegurarle que fue una verdadera PIA. Empecé con pip django-webpack-loader , luego https://github.com/NdagiStanley/vue-django y luego Bootstrap 4 cosas. Sin embargo, el resultado final es mucho más poderoso que el independiente, en mi opinión.

 /* 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

Finalmente, estamos listos para mostrar algunos contenidos reales:

bme-nav-item y bme-tab-pane son 2 componentes de Vue personalizados que utilizo para Boostrap 4 tab navs y contenidos.

Django usa var settings= some-json-object para comunicar datos específicos de la instancia, en lugar de la página, a Vue y JS

 {% extends "mysite/__full12_vue.html" %} <script> // KEY: settings is provided by json.dumps(some_settings_dictionary) // which your views puts into your RequestContext. // this is how Django tells Vue what changes with different data, on the same view var settings = {{settings | safe}}; </script> {% block content %} <!-- a button to run a Celery batch via a post command, url should probably come from Django url reverse and be put into a Vue property... --> <button v-bind:data-url="url_batch" type="button" class="btn btn-block btn-outline-primary" @click.prevent="run_batch"> <!-- lotsa stuff left out.... --> <ul id="tab-contents-nav" class="nav nav-tabs nav-pills"> <!-- *label* is using a Vue Prop and because there is no {% verbatim %} guard around it, Django will inject the contents. {% urlrev xxx %} could be used to write to an 'url' prop. Ditto the conditional inclusion, by Django, of a template if it's in the RequestContext. --> {% if templatename_details %} <bme-nav-item link="details" label="{{details_label}}" > </bme-nav-item> {% endif %} <!-- lotsa stuff left out.... --> <bme-tab-pane link="details"> <div slot="content"> <!-- KEY: Vue slots are incnetworkingibly powerful with Django. Basically this is saying to Django : inject what you want in the slot, using your include, I'll tidy up afterwards. In my case, this is a Bootstrap NavItem + NavTab --> {% 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'; //now Vue is using Vuex, which injects $store centralized state variables as needed Vue.use(Vuex); //KEY: re-using components defined once. import {base_messages, base_components} from '../mysite/commonbase.js'; var local_components = { //nothing, but I could have imported some other components to mix-n-match //in any case, bme-nav-item, bme-tab-pane and bme-user-messages need to //coming from somewhere for this page! }; const components = Object.assign({}, base_components, local_components); //we're going to put together a Vue on the fly... export function dovue(config) { //KEY: The store is a Vuex object - don't be fooled, it's not SPA-only // it's the easiest way to coherently share data across Vue Components, so use it. store.commit('initialize', config); //here I am telling the store which html IDs need hiding var li_tohide = settings.li_tohide || []; li_tohide.forEach(function(hidden) { store.commit('add_hidden', hidden); }); /* eslint-disable no-new */ var vm = new Vue({ //KEY: This tells the Vue instance what parts of your html are in its scope. el: '#bme-vue' //KEY: each bme-xxx and bme-yyy special tag needs to be found in components below //otherwise you'll see my SHOULDNT APPEAR IF VUE WORKED text in your page ,components: components ,data: { li_rowcount: window.settings.li_rowcount || [] ,csrf_token: window.csrf_token ,url_batch: "some url" } ,mounted: function () { // a Vue lifecycle hook. You could use to set up Vue Event listening for example console.log("data.js.lifecycle.mounted"); } ,methods : { ,run_batch: function(e) { //hook this up to a button console.assert(this.$data, COMPONENTNAME + ".run_batch.this.$data missing. check object types"); var url = e.target.dataset.url //this is defined elsewhere post_url_message(this, url, this.csrf_token); } } //the Vuex instance to use for this Vue. ,store: store }); //did Django provide any user messages that need displaying? var li_user_message = config.li_user_message || []; li_user_message.forEach(function(user_message, i) { //the bme-user-messages Component? It's listening for this event //and will display Bootstrap Alerts for them. vm.$emit(base_messages.EV_USER_MESSAGE, user_message); }); return vm; } //various app and page specific settings... import {app_config, LOCALNAV_LINK, TOPNAV_LINK_OTHERS} from "./constants"; var page_config = { //used to show which navigation items are .active localnav_link : LOCALNAV_LINK.data , topnav_link: TOPNAV_LINK_OTHERS.system_edit_currentdb }; //this is coming from Django's RequestContext. var generated_config = window.settings; //ok, now we are merging together a Django app level config, the page config and finally //what the Django view has put into settings. This will be passed to the Vuex store //individual Vue Components will then grab what they need from their $store attribute //which will point to that Vuex instance. var local_config = Object.assign({}, app_config, page_config, generated_config); var vm = dovue(local_config); 

vuex / generic.js:

Y una implementación de tienda ingenua, principalmente de solo lectura:

 //you can add your page's extra state, but this is a shanetworking baseline //for the site const state = { active_tab: "" ,topnav_link: "" ,localnav_link: "" ,li_user_message: [] ,s_visible_tabid: new Set() ,urls: {} }; const mutations = { //this is what your page-specific JS is putting into the state. initialize(state, config){ //initialize the store to a given configuration //KEY: attributes that did not exist on the state in the first place wont be reactive. // console.log("store.initialize"); Object.assign(state, config); }, //a sample mutation set_active_tab(state, tabid){ //which bme-tab-nav is active? if (! state.s_visible_tab.has(tabid)){ return; } state.active_tab = tabid; }, }; export {state as generic_state, mutations}; 

y para darle una idea de la jerarquía general de files:

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

De acuerdo, tuve que modificar la plantilla base del sitio para asegurarme de que div # bme-vue esté siempre disponible.

Probablemente se necesite un poco de refactorización entre this y mysite / __ full12_vue.html .

mysite / __ full12.html :

 <!-- lots of stuff left out --> <body> <!-- KEY: the #bme-vue wrapper/css selector tells Vue what's in scope. it needs to span as much of the <body> as possible, but also end right BEFORE the render_bundle tag. I set that css selector in mydjangoappname/some_django_view.js and I'd want to use the same selector everywhere throughout my app. --> <div id="bme-vue"> <!-- anything that ends up here , including through multiple nested/overridden Django content blocks , can be processed by Vue , but only when have Vue-relevant markup such as custom component tags or v-for directives. --> ...more blocks... {% block search %} {% endblock search %} <div id="main" role="main"> <div> <!-- class="container"> --> {% block content %} {% endblock %} </div> </div> ...more blocks... </div> <!-- bme-vue --> {%block extra_js_body_end%} {%endblock extra_js_body_end%} </body> </html> 
  • Forma preferida de usar Bootstrap en Webpack
  • vue no está definido en la instancia sino que se hace reference durante el procesamiento
  • Vue v-on: el clic no funciona en el componente
  • Seleccione el filter Vue basado en la variable
  • Desplázate hasta la parte inferior de div con Vue.js
  • ¿Valores pnetworkingeterminados para los accesorios del componente Vue y cómo verificar si un usuario no configuró el accesorio?
  • Cómo implementar scroll infinito en mi vue js
  • Forma correcta de crear componentes Vue que se basen en las herramientas de compilation primaria
  • VueJS: evita el valor pnetworkingeterminado onclick en el enlace, pero también captura el object
  • ¿Cómo usar Vue.js en Hexo (generador de sitios estáticos)?
  • Envío de formulario en Vue.js con ajax
  • Vue.js observa una propiedad anidada dentro de la matriz
  • 'window' no está definido en Vue.js 2
  • Javascript tiene muchos buenos JS marco (como Node.js AngularJS Vue.js React.js) es el mejor lenguaje de script.