Erstellen von Vue.js-Komponenteninstanzen programmatisch

Avatar of Kushagra Gour
Kushagra Gour on

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

Dieser Artikel setzt ein grundlegendes Verständnis des Vue.js-Frameworks und der Erstellung von Komponenten darin voraus. Wenn Sie neu bei Vue sind, dann ist diese CSS-Tricks-Reihe ein guter Ausgangspunkt.

Ich war an einem Vue.js-Projekt beteiligt, das die Fähigkeit erforderte, Komponenten programmatisch zu erstellen. Mit programmatisch meine ich, dass Sie die Komponenten vollständig aus JavaScript erstellen und einfügen, ohne etwas in der Vorlage zu schreiben. Dieser Artikel zielt darauf ab zu veranschaulichen, wie sich verschiedene Aspekte der Verwendung von Komponenten in einer Vorlage, wie z. B. Instanziierung, Props-Übergabe, Slots, Mounten, in JavaScript-Code übersetzen.

Normalerweise, wenn Sie mit dem empfohlenen Stil der **Single File Component** arbeiten, hätten Sie eine Komponente namens Button wie folgt:

<template>
 <button :class="type"><slot /></button>
</template>
<script>
 export default {
   name: 'Button',
   props: [ 'type' ],
 }
</script>

Um sie in einer anderen Komponente zu verwenden, müssen Sie lediglich die Button-Komponente importieren und ihr Tag in der Vorlage einfügen.

<template>
 <Button type="primary">Click me!</Button>
</template>
<script>
 import Button from 'Button.vue'
 export default {
   name: 'App',
   components: { Button }
 }
</script>

In meinem Fall weiß ich nicht, welche Komponente in der Vorlage eingefügt werden soll und wo sie eingefügt werden soll. Diese Information ist erst zur Laufzeit verfügbar. Also brauchte ich eine Möglichkeit, eine dynamische Komponenteninstanz für jede übergebene Komponente zu erstellen und sie zur Laufzeit in das DOM einzufügen.

Instanz erstellen

Der allererste Gedanke, den ich hatte, um eine dynamische Instanz einer gegebenen Komponente zu erstellen, war, sie an new zu übergeben, und es würde mir die tatsächliche Instanz geben. Aber wenn Sie genau hinsehen, sind die script-Blöcke in jedem der obigen Komponentencodes allesamt ein einfaches Objekt und keine Klasse (Konstruktorfunktion). Wenn ich das mache

import Button from 'Button.vue'
var instance = new Button()

... schlägt es fehl. Wir brauchen eine Klasse. Oder, einfach ausgedrückt, eine Konstruktorfunktion. Der Weg dorthin besteht darin, das Komponentenobjekt an Vue.extend zu übergeben, um eine Unterklasse des Vue-Konstruktors zu erstellen. Jetzt können wir daraus mit dem new-Schlüsselwort eine Instanz erstellen.

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass()

Hurra! Schritt 1 erledigt! Jetzt müssen wir es in das DOM einfügen.

In das DOM einfügen

Jede Vue-Instanz hat eine Methode namens $mount, die die Komponenteninstanz auf dem Element montiert, das Sie ihr übergeben (d. h. sie ersetzt das übergebene Element durch die Komponenteninstanz). Das war kein gewünschtes Verhalten. Ich wollte meine Komponenteninstanzen in ein DOM-Element einfügen. Dafür gibt es auch einen Weg. Aus der offiziellen Dokumentation:

Wenn das Argument elementOrSelector nicht angegeben wird, wird die Vorlage als Element außerhalb des Dokuments gerendert, und Sie müssen die native DOM-API verwenden, um sie selbst in das Dokument einzufügen.

Das habe ich getan.

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass()
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

Im obigen Code gibt es ein paar Dinge zu beachten.

Erstens ist $refs die empfohlene Methode, um eine Referenz auf ein DOM-Element in Vue.js zu erhalten. Sie geben ein Attribut für das DOM-Element an, auf das Sie verweisen möchten (in diesem Fall <div ref="container"></div>), und dieses Element ist dann über den angegebenen Schlüssel auf der $refs-Eigenschaft der Komponente verfügbar.

Zweitens können Sie die Eigenschaft $el verwenden, um die native DOM-Elementreferenz von einer Vue-Komponenteninstanz zu erhalten.

Props an die Instanz übergeben

Als Nächstes musste ich meiner Button-Instanz einige Props übergeben. Insbesondere den type-Prop. Die Vue-Konstruktorfunktion akzeptiert ein Optionenobjekt, das wir verwenden können, um zugehörige Dinge zu übergeben und zu initialisieren. Für die Übergabe von Props gibt es einen Schlüssel namens propsData, den wir wie folgt verwenden können:

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass({
    propsData: { type: 'primary' }
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

Wir sind fast fertig, mit einem letzten verbleibenden Teil. Bei der normalen Vorlagenmethode haben wir den Button wie folgt verwendet: <Button>Klick mich!</Button>;. Wir haben einfach den inneren Text zwischen den Tags geschrieben, und er wurde mithilfe von slot im endgültigen Button-Tag gerendert. Aber wie übergeben wir ihn jetzt?

Slot festlegen

Wenn Sie Slots in Vue.js verwendet haben, wissen Sie vielleicht, dass die Slots auf jeder Instanz über die Eigenschaft $slots zugänglich sind. Und wenn keine benannten Slots verwendet werden, sind die Slots als Array unter $slots.default verfügbar. Das ist genau der Schlüssel, den wir auf der Instanz ändern werden, um den inneren Text unseres Buttons festzulegen. Denken Sie daran, dass dies vor dem Mounten der Instanz geschehen muss. Beachten Sie, dass wir in unserem Fall nur einen einfachen String in den Slot gesetzt haben, weil das alles war, was wir brauchten. Sie können ihm aber auch komplexere DOM-Strukturen in Form von virtuellen Knoten oder VNodes unter Verwendung der Funktion createElement übergeben. Sie können hier über die Erstellung von virtuellen Knoten in der Vue.js-Dokumentation lesen. Dank eines Mitglieds des Vue-Kernteams, Rahul, für die Empfehlung dieser Technik.

import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass({
    propsData: { type: 'primary' }
})
instance.$slots.default = [ 'Click me!' ]
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)

Finale Demo

Demo ansehen

Ich hoffe, dieser Artikel hat Ihnen gefallen. Folgen Sie mir auf Twitter, wo ich weitere Artikel und meine Nebenprojekte teile.