So organisieren Sie Ihre Abhängigkeiten in einer Vue-Anwendung

Jeder, der mit Vue vertraut ist, weiß, dass eine Vue-Anwendung einen Einstiegspunkt hat - die Datei main.js Dort gibt es neben dem Erstellen einer Instanz von Vue einen Import und eine Art Abhängigkeitsinjektion aller Ihrer globalen Abhängigkeiten (Direktiven, Komponenten, Plugins). Je größer das Projekt, desto mehr Abhängigkeiten werden, die darüber hinaus jeweils ihre eigene Konfiguration haben. Als Ergebnis erhalten wir eine große Datei mit allen Konfigurationen.
In diesem Artikel wird erläutert, wie Sie globale Abhängigkeiten organisieren, um dies zu vermeiden.



Warum selbst schreiben?


Viele mögen denken - warum ist das notwendig, wenn es zum Beispiel Nuxt gibt , das dies für Sie erledigt ? In meinen Projekten habe ich es auch verwendet, aber in einfachen Projekten kann dies überflüssig sein. Darüber hinaus hat niemand Projekte mit Legacy-Code abgebrochen, die wie Schnee auf Ihrem Kopf auf Sie fallen. Und schließen Sie das Framework dort an - praktisch von Grund auf neu.

Mastermind


Der Organisator einer solchen Organisation war Nuxt. Es wurde von mir bei einem großen Projekt mit Vue verwendet.
Nuxt hat eine großartige Funktion - Plugins. Jedes Plugin ist eine Datei, die eine Funktion exportiert. Die Konfiguration wird an die Funktion übergeben, die beim Erstellen der Instanz auch an den Vue-Konstruktor sowie an den gesamten Speicher übergeben wird .

Darüber hinaus ist in jedem Plugin eine äußerst nützliche Funktion, inject verfügbar. Es wird eine Abhängigkeitsinjektion für die Stamminstanz von Vue und für das store . Dies bedeutet, dass in jeder Komponente, in jeder Speicherfunktion die angegebene Abhängigkeit dadurch verfügbar ist.

Wo kann das nützlich sein?


Zusätzlich zu der Tatsache, dass main.js erheblich „abnehmen“, erhalten Sie auch die Möglichkeit, die Abhängigkeit überall in der Anwendung ohne unnötige Importe zu verwenden.

Ein Paradebeispiel für Dependency Injection ist der Vue-Router . Es wird nicht sehr oft verwendet - um die Parameter der aktuellen Route abzurufen, um eine Umleitung durchzuführen, aber dies ist eine globale Abhängigkeit. Wenn es in einer Komponente nützlich sein kann, warum nicht global machen? Dank dessen wird der Status außerdem global gespeichert und für die gesamte Anwendung geändert.

Ein weiteres Beispiel ist vue-wait . Die Entwickler dieses Plugins gingen noch weiter und fügten die $wait Eigenschaft nicht nur der Vue-Instanz, sondern auch dem Vuex-Store hinzu. Angesichts der Besonderheiten des Plugins erweist sich dies als äußerst nützlich. Beispielsweise verfügt der Speicher über eine Aktion, die für mehrere Komponenten aufgerufen wird. In jedem Fall müssen Sie den Loader für ein Element anzeigen. Anstatt vor und nach jedem Aktionsaufruf $wait.start('action') und $wait.end('action') aufzurufen, können Sie diese Methoden einfach einmal in der Aktion selbst aufrufen. Und dies ist viel lesbarer und weniger ausführlich als der dispatch('wait/start', 'action' {root: true}) . Im Falle eines Geschäfts ist dies syntaktischer Zucker.

Von Wörtern zu Code


Die Grundstruktur des Projekts


Mal sehen, wie das Projekt jetzt aussieht:
src
- store
- App.vue
- main.js

main.js sieht main.js aus:
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; new Vue({ render: h => h(App), store }).$mount('#app'); 


Wir verbinden die erste Abhängigkeit


Jetzt wollen wir Axios mit unserem Projekt verbinden und eine Konfiguration dafür erstellen. Ich folgte der Nuxt-Terminologie und erstellte ein plugins Verzeichnis in src . Im Verzeichnis befinden sich die axios.js index.js und axios.js .

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

Wie oben erwähnt, muss jedes Plugin eine Funktion exportieren. Gleichzeitig möchten wir innerhalb der Funktion Zugriff auf den Speicher und anschließend auf die inject Funktion haben.

axios.js
 import axios from 'axios'; export default function (app) { //       – , , interceptors  .. axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; axios.interceptors.request.use(config => { ... return config; }); } 

index.js :
 import Vue from 'vue'; import axios from './axios'; export default function (app) { let inject = () => {}; //   inject,        Dependency Injection axios(app, inject); //       Vue    } 


Wie Sie sehen können, exportiert die Datei index.js auch die Funktion. Dies geschieht, um das app Objekt dort übergeben zu können. Lassen Sie uns nun main.js ändern und diese Funktion aufrufen.

main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; //    // ,    Vue,  ,     initPlugins const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); //   initPlugins    


Ergebnis


Zu diesem Zeitpunkt haben wir erreicht, dass wir die Plugin-Konfiguration aus main.js in einer separaten Datei entfernt haben.

Der Vorteil der Übergabe des app Objekts an alle unsere Plugins besteht übrigens darin, dass wir in jedem Plugin jetzt Zugriff auf den Store haben. Sie können es frei verwenden, indem Sie commit und dispatch aufrufen sowie auf store.state und store.getters .

Wenn Sie ES6-Stil mögen, können Sie dies sogar tun:

axios.js
 import axios from 'axios'; export default function ({store: {dispatch, commit, state, getters}}) { ... } 

Zweite Stufe - Abhängigkeitsinjektion


Wir haben bereits das erste Plugin erstellt und jetzt sieht unser Projekt so aus:

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

Da in den meisten Bibliotheken, in denen dies wirklich erforderlich ist, Dependency Injection bereits mit Vue.use implementiert ist, Vue.use wir unser eigenes einfaches Plugin.

Versuchen Sie beispielsweise zu wiederholen, was vue-wait tut. Dies ist eine ziemlich umfangreiche Bibliothek. Wenn Sie also den Loader auf zwei Schaltflächen anzeigen möchten, ist es besser, ihn zu verlassen. Ich konnte jedoch seiner Bequemlichkeit nicht widerstehen und wiederholte in seinem Projekt seine grundlegende Funktionalität, einschließlich des syntaktischen Zuckers im Laden.

Warten Sie Plugin


Erstellen Sie eine weitere Datei im plugins Verzeichnis - wait.js

Ich habe bereits ein Vuex-Modul, das ich auch wait . Er macht drei einfache Schritte:

- start - setzt die state-Eigenschaft eines Objekts mit dem Namen action auf true
- end - Entfernt eine Eigenschaft eines Objekts mit dem Namen action aus dem Status
- is - ruft vom Status eine Eigenschaft eines Objekts mit dem Namen action

In diesem Plugin werden wir es verwenden.

wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


Und verbinden Sie unser Plugin:

index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = () => {}; Injection axios(app, inject); wait(app, inject); } 


Injizieren Sie die Funktion


Jetzt implementieren wir die inject Funktion.
 //   2 : // name – ,       this.  ,   Vue        Dependency Injection // plugin – ,       this.  ,  ,           let inject = (name, plugin) => { let key = `$${name}`; //      app[key] = plugin; //     app app.store[key] = plugin; //     store //  Vue.prototype Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; 


Die Magie von Vue.prototype


Nun zur Magie. Die Vue-Dokumentation besagt, dass es ausreicht, Vue.prototype.$appName = ' '; zu schreiben Vue.prototype.$appName = ' '; und $appName werden hier verfügbar sein.

In Wirklichkeit stellte sich jedoch heraus, dass dies nicht der Fall ist. Aufgrund von Googeln gab es keine Antwort, warum ein solches Design nicht funktionierte. Daher habe ich mich entschlossen, die Autoren des Plugins zu kontaktieren, die dies bereits implementiert haben.

Globales Mixin


Wie in unserem Beispiel habe ich mir den vue-wait Plugin-Code angesehen. Sie bieten eine solche Implementierung an (der Quellcode wird aus Gründen der Übersichtlichkeit bereinigt):

 Vue.mixin({ beforeCreate() { const { wait, store } = this.$options; let instance = null; instance.init(Vue, store); // inject to store this.$wait = instance; // inject to app } }); 

Anstelle eines Prototyps wird vorgeschlagen, ein globales Mixin zu verwenden. Der Effekt ist vielleicht im Grunde der gleiche, vielleicht mit Ausnahme einiger Nuancen. Da die Injektion jedoch hier im Geschäft erfolgt, sieht sie nicht genau richtig aus und entspricht überhaupt nicht der Dokumentation.

Aber was ist, wenn Prototyp?


Die Idee hinter der im inject verwendeten Prototyplösung wurde von Nuxt übernommen. Es sieht viel richtiger aus als das globale Mixin, also habe ich mich darauf festgelegt.

  Vue.use(() => { // ,        if (Vue.prototype.hasOwnProperty(key)) { return; } //    ,         app  Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; //  ,    this } }); }); 


Ergebnis


Nach diesen Manipulationen erhalten wir die Möglichkeit, darauf zuzugreifen this.$wait von jeder Komponente sowie von jeder Methode im Geschäft.

Was ist passiert?


Projektstruktur:

src
- plugins
-- index.js
-- axios.js
-- wait.js
- store
- App.vue
- main.js


index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = (name, plugin) => { let key = `$${name}`; app[key] = plugin; app.store[key] = plugin; Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; axios(app, inject); wait(app, inject); } 


wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


axios.js
 import axios from 'axios'; export default function (app) { axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; } 


main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); 

Fazit


Als Ergebnis der Manipulationen haben wir einen Import- und einen Funktionsaufruf in der Datei main.js . Und jetzt ist sofort klar, wo für jedes Plugin und jede globale Abhängigkeit nach der Konfiguration gesucht werden muss.

Wenn Sie ein neues Plugin hinzufügen, müssen Sie nur eine Datei erstellen, die die Funktion exportiert, in index.js und diese Funktion aufrufen.

In meiner Praxis hat sich eine solche Struktur als sehr praktisch erwiesen, außerdem kann sie leicht von Projekt zu Projekt übertragen werden. Jetzt gibt es keine Schmerzen mehr, wenn Sie Dependency Injection ausführen oder ein anderes Plugin konfigurieren müssen.

Teilen Sie Ihre Erfahrungen mit dem Abhängigkeitsmanagement in den Kommentaren mit. Erfolgreiche Projekte!

Source: https://habr.com/ru/post/de423013/


All Articles