Verwendung von Mixins in Vue.js

Avatar of Sarah Drasner
Sarah Drasner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es ist eine häufige Situation: Sie haben zwei Komponenten, die sich sehr ähnlich sind, die gleiche grundlegende Funktionalität teilen, aber es gibt genug Unterschiede zwischen ihnen, dass Sie an einen Scheideweg gelangen: Teilen Sie diese Komponente in zwei verschiedene Komponenten auf? Oder behalten Sie eine Komponente bei, aber erstellen Sie genügend Varianz mit Props, um jede davon zu verändern?

Keine dieser Lösungen ist perfekt: Wenn Sie sie in zwei Komponenten aufteilen, riskieren Sie, dass Sie sie an zwei Stellen aktualisieren müssen, wenn sich die Funktionalität jemals ändert, was den DRY-Grundsatz untergräbt. Auf der anderen Seite können zu viele Props sehr schnell unübersichtlich werden und den Wartenden, selbst wenn es sich um Sie selbst handelt, zwingen, viel Kontext zu verstehen, um sie zu verwenden, was Sie verlangsamen kann.

Hier kommen Mixins ins Spiel. Mixins in Vue sind nützlich für das Schreiben in einem funktionalen Stil, da funktionale Programmierung letztendlich darauf abzielt, Code durch Reduzierung beweglicher Teile verständlich zu machen. (Es gibt ein tolles Zitat von Michael Feathers dazu). Ein Mixin ermöglicht es Ihnen, ein Stück Funktionalität zu kapseln, damit Sie es in verschiedenen Komponenten Ihrer Anwendung verwenden können. Wenn sie korrekt geschrieben sind, sind sie rein; sie modifizieren oder ändern keine Dinge außerhalb des Geltungsbereichs der Funktion, sodass Sie bei mehreren Ausführungen mit denselben Eingaben zuverlässig immer denselben Wert erhalten. Dies kann sehr mächtig sein.

Grundlegendes Beispiel

Nehmen wir an, wir haben ein paar verschiedene Komponenten, deren Aufgabe es ist, einen booleschen Zustand, ein Modal und einen Tooltip umzuschalten. Diese Tooltips und Modals haben nicht viel gemeinsam, abgesehen von dieser Funktionalität: Sie sehen nicht gleich aus, sie werden nicht gleich verwendet, aber die Logik ist ähnlich.

//modal
const Modal = {
  template: '#modal',
  data() {
    return {
      isShowing: false
    }
  },
  methods: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  },
  components: {
    appChild: Child
  }
}

//tooltip
const Tooltip = {
  template: '#tooltip',
  data() {
    return {
      isShowing: false
    }
  },
  methods: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  },
  components: {
    appChild: Child
  }
}

Wir könnten die Logik hier extrahieren und etwas erstellen, das wiederverwendet werden kann

const toggle = {
  data() {
    return {
      isShowing: false
    }
  },
  methods: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  }
}

const Modal = {
  template: '#modal',
  mixins: [toggle],
  components: {
    appChild: Child
  }
};

const Tooltip = {
  template: '#tooltip',
  mixins: [toggle],
  components: {
    appChild: Child
  }
};

Siehe den Pen Mixin von Sarah Drasner (@sdras) auf CodePen.

Dieses Beispiel wurde absichtlich klein und einfach gehalten, um die Lesbarkeit zu gewährleisten. Beispiele für Mixins, die sich in realen Anwendungen als nützlich erwiesen haben, sind unter anderem: Abrufen von Dimensionen des Viewports und der Komponente, Erfassen spezifischer mousemove-Ereignisse und Basiselemente von Diagrammen. Paul Pflugradt hat ein schönes Repository mit Vue Mixins, aber es ist erwähnenswert, dass diese in Coffeescript geschrieben sind.

Verwendung

Dieser Pen zeigt nicht wirklich, wie wir das in einer echten Anwendung einrichten würden, also schauen wir uns das als Nächstes an.

Sie können Ihre Verzeichnisstruktur beliebig einrichten, aber ich erstelle gerne ein Mixin-Verzeichnis, um organisiert zu bleiben. Die Datei, die wir erstellen würden, hätte eine .js-Erweiterung (im Gegensatz zu .vue, wie unsere anderen Dateien), und wir würden ein Objekt für das Mixin exportieren

directory structure shows mixins in a folder in components directory

Und dann hätten wir in Modal.vue darauf Zugriff, indem wir den Schalter wie folgt importieren:

import Child from './Child'
import { toggle } from './mixins/toggle'

export default {
  name: 'modal',
  mixins: [toggle],
  components: {
    appChild: Child
  }
}

Es ist wichtig zu verstehen, dass Lebenszyklusmethoden auch dann für uns verfügbar sind, wenn wir ein Objekt und keine Komponente verwenden. Wir könnten mounted() hier einhaken und es würde in den Lebenszyklus der Komponente übernommen werden, was diese Arbeitsweise wirklich flexibel und leistungsstark macht.

Zusammenführung

Wenn wir uns das letzte Beispiel ansehen, können wir erkennen, dass uns nicht nur unsere Funktionalität, sondern auch Lebenszyklus-Hooks aus dem Mixin zur Verfügung stehen. Wenn wir sie also auf eine Komponente mit überlappenden Prozessen anwenden, ist die Reihenfolge wichtig. Standardmäßig werden die Mixins zuerst angewendet und die Komponente danach, sodass wir sie bei Bedarf überschreiben können. Die Komponente hat das letzte Wort. Dies wird nur dann wirklich wichtig, wenn ein Konflikt besteht und die Komponente "entscheiden" muss, welche davon gewinnt. Andernfalls werden alle Elemente in ein Array platziert, um ausgeführt zu werden, und das Mixin wird zuerst hinzugefügt, die Komponente danach.

//mixin
const hi = {
  mounted() {
    console.log('hello from mixin!')
  }
}

//vue instance or component
new Vue({
  el: '#app',
  mixins: [hi],
  mounted() {
    console.log('hello from Vue instance!')
  }
});

//Output in console
> hello from mixin!
> hello from Vue instance!

Wenn die beiden kollidieren, können wir sehen, wie die Vue-Instanz oder -Komponente gewinnt

//mixin
const hi = {
  methods: {
    sayHello: function() {
      console.log('hello from mixin!')
    }
  },
  mounted() {
    this.sayHello()
  }
}

//vue instance or component
new Vue({
  el: '#app',
  mixins: [hi],
  methods: {
    sayHello: function() {
      console.log('hello from Vue instance!')
    }
  },
  mounted() {
    this.sayHello()
  }
})

// Output in console
> hello from Vue instance!
> hello from Vue instance!

Sie bemerken vielleicht, dass wir hier zwei console.logs für den Vue-Instanz-String anstelle von einem haben – das liegt daran, dass die erste aufgerufene Funktion nicht zerstört wurde, sondern überschrieben wurde. Wir rufen hier immer noch beide sayHello()-Funktionen auf.

Globale Mixins

Wenn wir den Begriff global in Bezug auf Mixins verwenden, beziehen wir uns nicht auf die Möglichkeit, sie in jeder Komponente abrufen zu können, wie wir es bei Filtern tun. Wir können unsere Mixins bereits in einer Komponente mit mixins: [toggle] abrufen.

Globale Mixins werden buchstäblich auf jede einzelne Komponente angewendet. Aus diesem Grund ist der Anwendungsfall dafür extrem begrenzt und sie sollten mit äußerster Vorsicht betrachtet werden. Ein Anwendungsfall, der Sinn ergibt, ist etwas wie ein Plugin, bei dem Sie möglicherweise Zugriff auf alles benötigen. Aber auch hier wäre ich vorsichtig, was Sie anwenden, insbesondere wenn Sie die Funktionalität auf Anwendungen erweitern, die für Sie eine Blackbox sein könnten.

Um eine globale Instanz zu erstellen, würden wir sie über der Vue-Instanz platzieren. In einem typischen Vue-CLI-Build würde dies in Ihrer main.js-Datei erfolgen.

Vue.mixin({
  mounted() {
    console.log('hello from mixin!')
  }
})

new Vue({
  ...
})

Auch hier mit Vorsicht verwenden! Dieser console.log würde nun in jeder einzelnen Komponente erscheinen. Das ist in diesem Fall nicht so schlimm (abgesehen vom ganzen Rauschen in der Konsole), aber Sie können sehen, wie potenziell schädlich das bei falscher Anwendung sein könnte.

Fazit

Mixins können nützlich sein, um ein kleines Stück Funktionalität zu kapseln, das Sie wiederverwenden möchten. Sie sind sicherlich nicht die einzige verfügbare Option: Higher-Order-Komponenten ermöglichen es Ihnen beispielsweise, ähnliche Funktionalitäten zu komponieren. Dies ist nur eine Arbeitsweise. Ich mag Mixins, weil wir keine Zustände weitergeben müssen, aber dieses Muster kann sicherlich auch missbraucht werden, also denken Sie sorgfältig darüber nach, welche Option für Ihre Anwendung am sinnvollsten ist.