Aufbau von „renderless“ Vue Komponenten

Avatar of Samuel Oloruntoba
Samuel Oloruntoba on

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

Es gibt diese beliebte Analogie für Vue, die so lautet: Vue ist das Ergebnis, wenn React und Angular zusammenkommen und ein Baby zeugen. Dieses Gefühl hatte ich schon immer. Bei der geringen Lernkurve von Vue ist es kein Wunder, dass es so viele Leute lieben. Da Vue versucht, dem Entwickler so viel Macht wie möglich über Komponenten und deren Implementierung zu geben, hat dieses Empfinden zum heutigen Thema geführt.

Der Begriff renderless components (komponentenlose Komponenten) bezieht sich auf Komponenten, die nichts rendern. In diesem Artikel werden wir behandeln, wie Vue das Rendern einer Komponente handhabt.

Wir werden auch sehen, wie wir die render() Funktion verwenden können, um komponentenlose Komponenten zu erstellen.

Um diesen Artikel am besten zu verstehen, sollten Sie ein wenig über Vue wissen. Wenn Sie neu sind, Sarah Drasner hilft Ihnen. Die offizielle Dokumentation ist ebenfalls eine sehr gute Ressource.

Enthüllung, wie Vue eine Komponente rendert

Vue bietet mehrere Möglichkeiten, das Markup einer Komponente zu definieren. Es gibt

  • Single File Components, die es uns ermöglichen, Komponenten und ihr Markup wie eine normale HTML-Datei zu definieren.
  • Die Komponenten-Eigenschaft template, die es uns erlaubt, JavaScript-Template-Literale zu verwenden, um das Markup für unsere Komponente zu definieren.
  • Die Komponenten-Eigenschaft el weist Vue an, das DOM nach Markup zu durchsuchen, das als Template verwendet werden soll.

Sie haben wahrscheinlich schon gehört (vielleicht eher nervig): Am Ende des Tages sind Vue und all seine Komponenten nur JavaScript. Angesichts der Menge an HTML und CSS, die wir schreiben, kann ich verstehen, warum Sie dieser Aussage widersprechen würden. Beispiel: Single File Components.

Mit Single File Components können wir eine Vue-Komponente wie folgt definieren:

<template>
  <div class="mood">
    {{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }}
  </div>
</template>

<script>
  export default {
    data: () => ({ todayIsSunny: true })
  }
</script>

<style>
  .mood:after {
    content: '&#x1f389;&#x1f389;';
  }
</style>

Wie können wir angesichts all des oben genannten Kauderwelschs sagen, dass Vue „nur JavaScript“ ist? Aber am Ende des Tages ist es das. Vue versucht, es uns Entwicklern leicht zu machen, unsere Ansichten, Stile und andere Assets zu verwalten, aber Vue tut das nicht direkt – es überlässt es dem Build-Prozess, der wahrscheinlich webpack ist.

Wenn webpack auf eine .vue-Datei stößt, durchläuft diese einen Transformationsprozess. Während dieses Prozesses wird das CSS aus der Komponente extrahiert und in eine eigene Datei gelegt, und der verbleibende Inhalt der Datei wird in JavaScript umgewandelt. Etwas wie das hier:

export default {
  template: `
    <div class="mood">
      {{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }}
    </div>`,
  data: () => ({ todayIsSunny: true })
}

Nun… nicht ganz das, was wir oben haben. Um zu verstehen, was als Nächstes passiert, müssen wir über den Template-Compiler sprechen.

Der Template-Compiler und die Render-Funktion

Dieser Teil des Build-Prozesses einer Vue-Komponente ist notwendig, um jede Optimierungstechnik zu kompilieren und auszuführen, die Vue derzeit implementiert.

Wenn der Template-Compiler auf Folgendes stößt:

{
  template: `<div class="mood">...</div>`,
  data: () => ({ todayIsSunny: true })
}

… extrahiert er die template-Eigenschaft und kompiliert deren Inhalt zu JavaScript. Anschließend wird der Komponentenschicht ein Objekt eine Render-Funktion hinzugefügt. Diese Render-Funktion gibt wiederum den extrahierten Inhalt der Template-Eigenschaft zurück, der in JavaScript umgewandelt wurde.

So sieht das obige Template als Render-Funktion aus:

...
render(h) {
  return h(
    'div',
    { class: 'mood' },
    this.todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me'
  )
}
...

Schauen Sie in die offizielle Dokumentation, um mehr über die Render-Funktion zu erfahren.

Wenn nun das Komponentenschicht-Objekt an Vue übergeben wird, durchläuft die Render-Funktion der Komponente einige Optimierungen und wird zu einem VNode (Virtual Node). VNode ist das, was an snabbdom (die Bibliothek, die Vue intern zur Verwaltung des virtuellen DOM verwendet) übergeben wird. Sarah Drasner erklärt gut, wofür das „h“ in der obigen Render-Funktion steht.

Ein VNode ist, wie Vue Komponenten rendert. Übrigens erlaubt die Render-Funktion uns auch, JSX in Vue zu verwenden!

Wir müssen nicht darauf warten, dass Vue die Render-Funktion hinzufügt – wir können selbst eine Render-Funktion definieren, und diese hat Vorrang vor el oder der template-Eigenschaft. Lesen Sie hier, um mehr über die Render-Funktion und ihre Optionen zu erfahren.

Indem Sie Ihre Vue-Komponenten mit Vue CLI oder einem benutzerdefinierten Build-Prozess erstellen, müssen Sie den Template-Compiler nicht importieren, was Ihre Build-Dateigröße aufblähen kann. Ihre Komponenten sind zudem für eine brillante Leistung voroptimiert und bestehen aus sehr leichten JavaScript-Dateien.

Also… Komponentenlose Vue-Komponenten

Wie ich bereits erwähnt habe, bedeutet der Begriff renderless components (komponentenlose Komponenten), dass Komponenten nichts rendern. Warum sollten wir Komponenten wollen, die nichts rendern?

Wir können es darauf zurückführen, eine Abstraktion gängiger Komponentenfunktionalität als eigene Komponente zu erstellen und diese Komponente zu erweitern, um bessere und noch robustere Komponenten zu erstellen. Außerdem S.O.L.I.D.

Gemäß dem Single Responsibility Principle von S.O.L.I.D.

Eine Klasse sollte nur einen Zweck haben.

Wir können dieses Konzept auf die Vue-Entwicklung übertragen, indem wir jeder Komponente nur eine einzige Verantwortung geben.

Sie sind vielleicht wie Nicky und denken: „Pff, ja, ich weiß.“ Okay, sicher! Ihre Komponente mag den Namen „password-input“ tragen und rendert sicherlich eine Passwort-Eingabe. Das Problem bei diesem Ansatz ist, dass Sie, wenn Sie diese Komponente in einem anderen Projekt wiederverwenden möchten, möglicherweise in die Quelldatei der Komponente gehen müssen, um den Stil oder das Markup zu ändern, um es an den Styleguide des neuen Projekts anzupassen.

Dies verstößt gegen eine Regel von S.O.L.I.D., bekannt als Open-Closed-Prinzip, das besagt:

Eine Klasse oder eine Komponente, in diesem Fall, sollte offen für Erweiterung, aber geschlossen für Modifikation sein.

Das bedeutet, dass Sie die Komponente erweitern können sollten, anstatt ihren Quellcode zu bearbeiten.

Da Vue die S.O.L.I.D.-Prinzipien versteht, lässt es Komponenten Props, Events, Slots und Scoped Slots haben, was die Kommunikation und Erweiterung einer Komponente zum Kinderspiel macht. Wir können dann Komponenten bauen, die alle Funktionen haben, aber ohne jegliches Styling oder Markup. Das ist wirklich großartig für Wiederverwendbarkeit und effizienten Code.

Erstellen einer „Toggle“-Komponente ohne Rendering

Dies wird einfach sein. Kein Vue CLI-Projekt muss eingerichtet werden.

Die Toggle-Komponente ermöglicht das Umschalten zwischen Zuständen „Ein“ und „Aus“. Sie stellt auch Hilfsfunktionen bereit, um dies zu tun. Sie ist nützlich für den Aufbau von Komponenten wie, nun ja, Ein/Aus-Komponenten wie benutzerdefinierten Checkboxen und jeder Komponente, die einen Ein/Aus-Zustand benötigt.

Lassen Sie uns unsere Komponente schnell skizzieren: Gehen Sie zum JavaScript-Bereich eines CodePen-Pens und folgen Sie mir.

// toggle.js
const toggle = {
  props: {
    on: { type: Boolean, default: false }
  },
  render() {
    return []
  },
  data() {
    return { currentState: this.on }
  },
  methods: {
    setOn() {
      this.currentState = true
    },
    setOff() {
      this.currentState = false
    },
    toggle() {
      this.currentState = !this.currentState
    }
  }
}

Das ist ziemlich minimal und noch nicht vollständig. Es benötigt ein Template, und da wir nicht wollen, dass diese Komponente etwas rendert, müssen wir sicherstellen, dass sie mit jeder Komponente funktioniert, die etwas rendert.

Hier kommen die Slots ins Spiel!

Slots in Komponenten ohne Rendering

Slots erlauben es uns, Inhalt zwischen den öffnenden und schließenden Tags einer Vue-Komponente zu platzieren. So:

<toggle>
  This entire area is a slot.
</toggle>

In den Single File Components von Vue könnten wir dies tun, um einen Slot zu definieren:

<template>
  <div>
    <slot/>
  </div>
</template>

Nun, um das mit einer render()-Funktion zu tun, können wir:

// toggle.js
render() {
  return this.$slots.default
}

Wir können automatisch Dinge in unsere Toggle-Komponente einfügen. Kein Markup, nichts.

Datenbaumaufwärts mit Scoped Slots senden

In toggle.js hatten wir einen on-Zustand und einige Hilfsfunktionen im methods-Objekt. Es wäre schön, wenn wir dem Entwickler Zugriff darauf geben könnten. Wir verwenden derzeit Slots, und diese erlauben uns nicht, etwas von der Kindkomponente preiszugeben.

Was wir wollen, sind Scoped Slots. Scoped Slots funktionieren genauso wie normale Slots, mit dem zusätzlichen Vorteil, dass eine Komponente mit einem Scoped Slot Daten preisgeben kann, ohne Events auszulösen.

Das können wir tun:

<toggle>
  <div slot-scope="{ on }">
    {{ on ? 'On' : 'Off' }}
  </div>
</toggle>

Das Attribut slot-scope auf dem div de-strukturiert ein Objekt, das von der Toggle-Komponente bereitgestellt wird.

Wenn wir zur render()-Funktion zurückkehren, können wir Folgendes tun:

render() {
  return this.$scopedSlots.default({})
}

Dieses Mal rufen wir die Standardeigenschaft des $scopedSlots-Objekts als Methode auf, da Scoped Slots Methoden sind, die ein Argument nehmen. In diesem Fall ist der Name der Methode default, da dem Scoped Slot kein Name gegeben wurde und er der einzige Scoped Slot ist, der existiert. Das Argument, das wir einem Scoped Slot übergeben, kann dann von der Komponente bereitgestellt werden. In unserem Fall lassen wir den aktuellen Zustand von on und die Hilfsfunktionen, um diesen Zustand zu manipulieren, preisgeben.

render() {
  return this.$scopedSlots.default({
    on: this.currentState,
    setOn: this.setOn,
    setOff: this.setOff,
    toggle: this.toggle,
  })
}

Verwendung der Toggle-Komponente

Wir können das alles in CodePen machen. Hier ist, was wir bauen:

Siehe den Pen ZRaYWm von Samuel Oloruntoba (@kayandrae07) auf CodePen.

Hier ist das Markup in Aktion:

<div id="app">
  <toggle>
    <div slot-scope="{ on, setOn, setOff }" class="container">
      <button @click="click(setOn)" class="button">Blue pill</button>
      <button @click="click(setOff)" class="button isRed">Red pill</button>
      <div v-if="buttonPressed" class="message">
        <span v-if="on">It's all a dream, go back to sleep.</span>
        <span v-else>I don't know how far the rabbit hole goes, I'm not a rabbit, neither do I measure holes.</span>
      </div>
    </div>
  </toggle>
</div>
  1. Zuerst de-strukturieren wir den Zustand und die Helfer aus dem Scoped Slot.
  2. Dann haben wir innerhalb des Scoped Slots zwei Buttons erstellt, einen, um den aktuellen Zustand auf „Ein“ zu schalten, und einen anderen, um ihn auf „Aus“ zu schalten.
  3. Die click-Methode ist nur vorhanden, um sicherzustellen, dass tatsächlich ein Button gedrückt wurde, bevor wir ein Ergebnis anzeigen. Sie können die Click-Methode unten einsehen.
new Vue({
  el: '#app',
  components: { toggle },
  data: {
    buttonPressed: false,
  },
  methods: {
    click(fn) {
      this.buttonPressed = true
      fn()
    },
  },
})

Wir können immer noch Props übergeben und Events von der Toggle-Komponente auslösen. Die Verwendung eines Scoped Slots ändert nichts.

new Vue({
  el: '#app',
  components: { toggle },
  data: {
    buttonPressed: false,
  },
  methods: {
    click(fn) {
      this.buttonPressed = true
      fn()
    },
  },
})

Dies ist ein einfaches Beispiel, aber wir können sehen, wie mächtig dies werden kann, wenn wir anfangen, Komponenten wie einen Datumsauswähler oder ein Autovervollständigungs-Widget zu bauen. Wir können diese Komponenten projektübergreifend wiederverwenden, ohne uns um die lästigen Stylesheets kümmern zu müssen, die im Weg sind.

Eine weitere Sache, die wir tun können, ist, die für die Zugänglichkeit benötigten Attribute aus dem Scoped Slot preiszugeben und uns auch keine Sorgen machen müssen, Komponenten, die diese Komponente erweitern, zugänglich zu machen.

Zusammenfassung

  • Die Render-Funktion einer Komponente ist unglaublich mächtig.
  • Erstellen Sie Ihre Vue-Komponenten für eine schnellere Laufzeit.
  • el, template oder sogar Single File Components einer Komponente werden alle in Render-Funktionen kompiliert.
  • Versuchen Sie, kleinere Komponenten für wiederverwendbareren Code zu erstellen.
  • Ihr Code muss nicht S.O.L.I.D. sein, aber es ist eine verdammt gute Methodik, um nach zu codieren.

Quellen