Vue-Plugin erstellen: install, provide und eigene Direktiven

Vue.js-Plugins sind ein mächtiger und zugleich einfacher Weg, deiner Anwendung globale Funktionen hinzuzufügen. Die Einsatzgebiete reichen von der anwendungsweiten Bereitstellung von Komponenten bis zu zusätzlichen Fähigkeiten wie Routing oder unveränderlichen Datenstrukturen. In diesem Artikel zeige ich dir

Vue-Plugin erstellen: install, provide und eigene Direktiven
Schnelle Antwort

Ein Vue-3-Plugin ist ein Objekt mit einer Methode install(app, options), das du über app.use() installierst. Es registriert Komponenten und Direktiven, stellt Werte per app.provide() bereit und konfiguriert die Anwendung. Vue-2-Code auf Basis von Vue.prototype und Vue.mixin funktioniert nicht mehr.

Ein Vue-Plugin erweitert eine ganze Anwendung: eine überall verfügbare Komponente, eine Direktive, ein injizierbarer Wert, eine gemeinsame Konfiguration. Es ist der Mechanismus hinter Vue Router, Pinia und den meisten Bibliotheken des Ökosystems.

Was ein Plugin ist

Ein Objekt mit einer Methode install oder schlicht eine Funktion. Vue ruft sie mit der Anwendungsinstanz und den Optionen auf, die du an app.use() übergibst.

src/plugins/monitor.js
export default {
  install(app, options) {
    // hier passiert alles
  }
}
Vue-2-Code läuft nicht mehr

In Vue 2 bekam install den globalen Konstruktor Vue, und ein Plugin arbeitete mit Vue.prototype, Vue.mixin oder Vue.component. In Vue 3 gibt es keinen globalen Konstruktor mehr: install erhält die von createApp erzeugte Instanz, und alles läuft über sie. Ein unverändert kopiertes Vue-2-Plugin scheitert mit Cannot set properties of undefined (setting '$api') oder bricht stillschweigend ab.

Die Gegenüberstellung:

javascript
// Vue 3
Vue.use(Plugin)                   app.use(Plugin)
Vue.component('X', X)             app.component('X', X)
Vue.directive('x', x)             app.directive('x', x)
Vue.mixin({ … })                  app.mixin({ … })
Vue.prototype.$api = api          app.config.globalProperties.$api = api
                                  app.provide('api', api)   // besser

Ein vollständiges Plugin

Das folgende Plugin protokolliert das Mounten der Komponenten, stellt dieses Protokoll bereit und liefert eine Direktive mit. Es zeigt die vier Ansatzpunkte, die du kennen solltest.

src/plugins/monitor.js
export default {
  install(app, options = {}) {
    const prefixe = options.prefixe ?? '[monitor]'
    const journal = []

    // 1. globale Eigenschaft, in der Options API über this erreichbar
    app.config.globalProperties.$journal = journal

    // 2. Injection: der empfohlene Weg mit <script setup>
    app.provide('journal', journal)

    // 3. globale Direktive
    app.directive('surligne', {
      mounted(el, binding) {
        el.style.backgroundColor = binding.value ?? '#fef08a'
      }
    })

    // 4. globaler Mixin, läuft beim Mounten jeder Komponente
    app.mixin({
      mounted() {
        journal.push(`${prefixe} ${this.$options.__name ?? 'anonyme'} monté`)
      }
    })
  }
}

Die Installation, mit oder ohne Optionen:

src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import monitor from './plugins/monitor.js'

const app = createApp(App)

app.use(monitor, { prefixe: '[gekkode]' })
app.mount('#app')

Ein zweimal installiertes Plugin wird nur einmal angewendet: Vue führt Buch über die Plugins, die bereits durch app.use() gelaufen sind.

provide statt globalProperties

app.config.globalProperties.$journal bildet die Vue-2-Gewohnheit this.$irgendwas nach. Das funktioniert, aber this gibt es in <script setup> nicht: Die Eigenschaft ist nur aus dem Template oder der Options API erreichbar. Autovervollständigung und Typisierung greifen dort ebenfalls nicht.

app.provide() ist der richtige Weg. Die Komponente holt sich den Wert über inject:

src/components/Journal.vue
<script setup>
import { inject } from 'vue'

const journal = inject('journal')
</script>

<template>
  <p>{{ journal.length }} composants montés</p>
</template>

Damit sich Namen als Zeichenketten nicht ins Gehege kommen, exportierst du aus dem Plugin ein Symbol:

src/plugins/monitor.js
export const cleJournal = Symbol('journal')

export default {
  install(app) {
    app.provide(cleJournal, [])
  }
}

Komponenten ausliefern

Der häufigste Fall: eine Bibliothek von Komponenten ohne Import verfügbar machen.

src/plugins/ui.js
import GkBouton from '../components/GkBouton.vue'
import GkCarte from '../components/GkCarte.vue'

export default {
  install(app, { prefixe = 'Gk' } = {}) {
    app.component(`${prefixe}Bouton`, GkBouton)
    app.component(`${prefixe}Carte`, GkCarte)
  }
}

Achte auf den Preis dieser Bequemlichkeit: Eine global registrierte Komponente ist überall verfügbar, entzieht sich aber dem Tree Shaking. Sie landet auch dann im Bundle, wenn sie niemand benutzt. Registriere global nur, was wirklich allgegenwärtig ist.

Ein Plugin darf eine einfache Funktion sein

Wenn dir das Objekt mit der Methode install zu umständlich ist, genügt eine Funktion: Vue ruft sie mit denselben Argumenten auf.

src/plugins/titre.js
export default function titrePlugin(app, options) {
  app.config.globalProperties.$titre = options.titre
}

Das veröffentlichungsfähige Paket

Probiere es zuerst in einem frischen Vite-Projekt aus. Für die Veröffentlichung auf npm exportierst du es als Default und lässt die Abhängigkeit auf Vue in den peerDependencies, damit das Hostprojekt seine eigene Kopie mitbringt:

package.json
{
  "name": "gekkode-monitor",
  "type": "module",
  "main": "./dist/index.js",
  "exports": { ".": "./dist/index.js" },
  "peerDependencies": { "vue": "^3.5.0" }
}
Die Selbstinstallation hat ausgedient

Vue-2-Plugins endeten oft mit if (window.Vue) window.Vue.use(Plugin), um sich selbst zu installieren, wenn die Seite Vue über ein script-Tag lud. In Vue 3 gibt es kein globales window.Vue mehr: Dieser Block ist nutzlos und gehört entfernt.

Der globale Mixin, als letztes Mittel

Das Beispiel-Plugin nutzt app.mixin, um jedes Mounten zu beobachten. Für ein querschnittliches Anliegen wie Telemetrie, bei dem du wirklich jede Komponente erwischen willst, ist das der richtige Griff.

Für alles andere nimmst du besser einen Composable: eine exportierte Funktion, die nur die interessierten Komponenten aufrufen. Ein globaler Mixin greift überall, auch dort, wo niemand damit rechnet, und macht die Herkunft eines Verhaltens schwer nachvollziehbar.

src/composables/useJournal.js
import { inject } from 'vue'

export function useJournal() {
  const journal = inject('journal')
  return {
    journal,
    ajouter: (message) => journal.push(message)
  }
}

Ein Plugin testen

Vue Test Utils nimmt Plugins samt Optionen in der Option global entgegen:

javascript
import { mount } from '@vue/test-utils'
import monitor from '@/plugins/monitor.js'

mount(MonComposant, {
  global: { plugins: [[monitor, { prefixe: '[test]' }]] }
})

Ohne Optionen reicht die Kurzform: plugins: [monitor].

Weiterlesen

Viele Bibliotheken des Ökosystems werden genau so installiert, von VueFire bis unhead für die Suchmaschinenoptimierung.

Plugins entfalten ihren Nutzen erst, wenn Komponenten und Injection sitzen. Sind dir diese Begriffe noch neu, nimm dir das Vue-3-Tutorial für Einsteiger vor, besonders die Kapitel zu Komponenten und Props. Für geteilten State ist Pinia selbst ein Plugin, und sein Quellcode lohnt die Lektüre.

Häufige Fehler

Ein Vue-2-Plugin unverändert übernehmen install bekommt die Anwendungsinstanz, nicht mehr den globalen Konstruktor. Vue.prototype gibt es nicht mehr: Nimm app.provide() oder app.config.globalProperties.
globalProperties mit script setup this gibt es in <script setup> nicht: Die Eigenschaft ist nur im Template lesbar. Besser provide und inject.
Den Block zur Selbstinstallation behalten if (window.Vue) window.Vue.use(Plugin) findet in Vue 3 nichts mehr vor und gehört entfernt.
Zu viele Komponenten global registrieren Sie entziehen sich dem Tree Shaking und blähen das Bundle auf, auch ungenutzt.

JavaScriptVue.js

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.