Was ist neu in Vue 3?

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Vue 3 enthält viele interessante neue Funktionen und Änderungen an einigen der vorhandenen, die darauf abzielen, die Entwicklung mit dem Framework viel einfacher und wartbarer zu machen. In diesem Artikel werfen wir einen Blick auf einige dieser neuen Funktionen und erfahren, wie Sie damit beginnen können. Wir werden uns auch einige der Änderungen ansehen, die an den bestehenden Funktionen vorgenommen wurden.

Mit der Veröffentlichung von Vue 3 müssen Entwickler das Upgrade von Vue 2 vornehmen, da es eine Handvoll neuer Funktionen enthält, die beim Erstellen von leicht lesbaren und wartbaren Komponenten und verbesserten Möglichkeiten zur Strukturierung unserer Anwendung in Vue sehr hilfreich sind. Wir werden uns einige dieser Funktionen in diesem Artikel ansehen.

Am Ende dieses Tutorials werden die Leser;

  1. Informieren Sie sich über provide / inject und wie Sie es verwenden.
  2. Haben Sie ein grundlegendes Verständnis von Teleport und wie man es benutzt.
  3. Informieren Sie sich über Fragmente und deren Verwendung.
  4. Informieren Sie sich über die Änderungen, die an der Global Vue API vorgenommen wurden.
  5. Informieren Sie sich über die an der Events-API vorgenommenen Änderungen.

Dieser Artikel richtet sich an diejenigen, die ein angemessenes Verständnis von Vue 2.x haben. Den gesamten in diesem Beispiel verwendeten Code finden Sie auf GitHub.

provide / inject

In Vue 2.x hatten wir props , die es einfach machten, Daten (Strings, Arrays, Objekte usw.) von einer übergeordneten Komponente direkt an ihre untergeordnete Komponente zu übergeben. Aber während der Entwicklung fanden wir oft Fälle, in denen wir Daten von der übergeordneten Komponente an eine tief verschachtelte Komponente übergeben mussten, was mit props schwieriger zu bewerkstelligen war. Dies führte zur Verwendung von Vuex Store, Event Hub und manchmal zum Weiterleiten von Daten durch die tief verschachtelten Komponenten. Schauen wir uns eine einfache App an;

Es ist wichtig zu beachten, dass Vue 2.2.0 auch mit provide / inject geliefert wurde, dessen Verwendung in generischem Anwendungscode nicht empfohlen wurde.

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { color: "", colors: ["red", "blue", "green"], }; }, }; </script>
 # childComponent.vue <template> <div class="hello"> <h1>{{ msg }}</h1> <color-selector :color="color"></color-selector> </div> </template> <script> import colorSelector from "@/components/colorComponent.vue"; export default { name: "HelloWorld", components: { colorSelector, }, props: { msg: String, color: String, }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { props: { color: String, }, }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Hier haben wir eine Zielseite mit einem Dropdown-Menü, das eine Liste von Farben enthält, und wir übergeben die ausgewählte color als Requisite an childComponent.vue . Diese untergeordnete Komponente hat auch eine msg -Prop, die einen Text akzeptiert, der im Vorlagenabschnitt angezeigt werden soll. Schließlich hat diese Komponente eine untergeordnete Komponente ( colorComponent.vue ), die eine color von der übergeordneten Komponente akzeptiert, die zum Bestimmen der Klasse für den Text in dieser Komponente verwendet wird. Dies ist ein Beispiel für die Weitergabe von Daten durch alle Komponenten.

Aber mit Vue 3 können wir dies auf sauberere und kürzere Weise tun, indem wir das neue Paar „Provide“ und „Inject“ verwenden. Wie der Name schon sagt, verwenden wir „ provide “ entweder als Funktion oder als Objekt, um Daten von einer übergeordneten Komponente für jede ihrer verschachtelten Komponenten verfügbar zu machen, unabhängig davon, wie tief eine solche Komponente verschachtelt ist. Wir verwenden das Objektformular, wenn wir hartcodierte Werte übergeben, provide diese bereitzustellen;

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { colors: ["red", "blue", "green"], }; }, provide: { color: 'blue' } }; </script>

Aber für Fälle, in denen Sie eine Komponenteninstanzeigenschaft übergeben müssen, um provide , verwenden wir den Funktionsmodus, damit dies möglich ist;

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "blue", colors: ["red", "blue", "green"], }; }, provide() { return { color: this.selectedColor, }; }, }; </script>

Da wir die color in childComponent.vue und colorComponent.vue nicht benötigen, werden wir sie los. Das Gute an der Verwendung von provide ist, dass die übergeordnete Komponente nicht wissen muss, welche Komponente die von ihr bereitgestellte Eigenschaft benötigt.

Um dies in der Komponente zu nutzen, die es in diesem Fall benötigt, colorComponent.vue , tun wir dies;

 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Hier verwenden wir inject , das ein Array der erforderlichen Variablen aufnimmt, die die Komponente benötigt. In diesem Fall benötigen wir nur die color , also übergeben wir nur diese. Danach können wir die color genauso verwenden, wie wir sie bei der Verwendung von Requisiten verwenden.

Wir stellen möglicherweise fest, dass die Farbe in colorComponent.vue nicht aktualisiert wird, wenn wir versuchen, eine neue Farbe über das Dropdown auszuwählen, und dies liegt daran, dass die Eigenschaften in der provide standardmäßig nicht reaktiv sind. Um das zu beheben, verwenden wir die computed .

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; import { computed } from "vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "", todos: ["Feed a cat", "Buy tickets"], colors: ["red", "blue", "green"], }; }, provide() { return { color: computed(() => this.selectedColor), }; }, }; </script>

Hier importieren wir computed und übergeben unsere selectedColor , damit sie reaktiv sein und aktualisiert werden kann, wenn der Benutzer eine andere Farbe auswählt. Wenn Sie eine Variable an die berechnete Methode übergeben, gibt sie ein Objekt zurück, das einen value hat. Diese Eigenschaft enthält den Wert Ihrer Variablen. Für dieses Beispiel müssten wir also colorComponent.vue so aktualisieren, dass es so aussieht.

 # colorComponent.vue <template> <p :class="[color.value]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Hier ändern wir color in color.value , um die Änderung darzustellen, nachdem color mit der computed Methode reaktiv gemacht wurde. An diesem Punkt würde sich die class des Textes in dieser Komponente immer dann ändern, wenn selectedColor sich in der Elternkomponente ändert.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Teleportieren

Es gibt Fälle, in denen wir Komponenten erstellen und sie aufgrund der von der App verwendeten Logik in einem Teil unserer Anwendung platzieren, aber dafür vorgesehen sind, in einem anderen Teil unserer Anwendung angezeigt zu werden. Ein gängiges Beispiel hierfür wäre ein Modal oder ein Popup, das den gesamten Bildschirm anzeigen und abdecken soll. Während wir hierfür eine Problemumgehung erstellen können, indem wir die position von CSS für solche Elemente verwenden, können wir mit Vue 3 auch Teleport verwenden.

Teleport ermöglicht es uns, eine Komponente aus ihrer ursprünglichen Position in einem Dokument aus dem standardmäßigen #app Container zu entfernen, in den Vue-Apps eingeschlossen sind, und sie zu einem beliebigen vorhandenen Element auf der Seite zu verschieben, auf der sie verwendet wird. Ein gutes Beispiel wäre die Verwendung von Teleport, um eine Header-Komponente aus dem #app Div in einen header zu verschieben. Es ist wichtig zu beachten, dass Sie nur zu Elementen teleportieren können, die außerhalb des Vue-DOM vorhanden sind.

Fehlermeldung in der Konsole, wenn Sie zu einem ungültigen Element teleportieren
Teleport-Fehlermeldung in der Konsole: Ungültige Teleport-Ziel-Fehlermeldung im Terminal. (Große Vorschau)

Die Teleport-Komponente akzeptiert zwei Requisiten, die das Verhalten dieser Komponente bestimmen, und das sind sie;

  1. to
    Diese Prop akzeptiert entweder einen Klassennamen, eine ID, ein Element oder ein data-*-Attribut. Wir können diesen Wert auch dynamisch machen, indem wir eine :to -Prop im Gegensatz zu to und das Teleport-Element dynamisch ändern.
  2. :disabled
    Diese Requisite akzeptiert einen Boolean und kann verwendet werden, um die Teleport-Funktion für ein Element oder eine Komponente umzuschalten. Dies kann nützlich sein, um die Position eines Elements dynamisch zu ändern.

Ein ideales Beispiel für die Verwendung von Teleport sieht so aus;

 # index.html** <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <title> <%= htmlWebpackPlugin.options.title %> </title> </head> <!-- add container to teleport to --> <header class="header"></header> <body> <noscript> <strong >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript> <div></div> <!-- built files will be auto injected --> </body> </html>

In der Standarddatei index.html in Ihrer Vue-App fügen wir ein header -Element hinzu, weil wir unsere Header-Komponente zu diesem Punkt in unserer App teleportieren möchten. Wir haben diesem Element auch eine Klasse zum Stylen und zur einfachen Referenzierung in unserer Teleport-Komponente hinzugefügt.

 # Header.vue** <template> <teleport to="header"> <h1 class="logo">Vue 3 </h1> <nav> <router-link to="/">Home</router-link> </nav> </teleport> </template> <script> export default { name: "app-header", }; </script> <style> .header { display: flex; align-items: center; justify-content: center; } .logo { margin-right: 20px; } </style>

Hier erstellen wir die Header-Komponente und fügen ein Logo mit einem Link zur Homepage unserer App hinzu. Wir fügen auch die Teleport-Komponente hinzu und geben der to -Prop den Wert header , weil wir möchten, dass diese Komponente in diesem Element gerendert wird. Schließlich importieren wir diese Komponente in unsere App;

 # App.vue <template> <router-view /> <app-header></app-header> </template> <script> import appHeader from "@/components/Header.vue"; export default { components: { appHeader, }, }; </script>

In diese Datei importieren wir die Header-Komponente und platzieren sie in der Vorlage, damit sie in unserer App sichtbar ist.

Wenn wir nun das Element unserer App untersuchen, würden wir feststellen, dass sich unsere Header-Komponente innerhalb des header Elements befindet;

Header-Komponente in DevTools
Header-Komponente in devtools (große Vorschau)

Fragmente

Mit Vue 2.x war es unmöglich, mehrere Root-Elemente in der template Ihrer Datei zu haben, und als Workaround begannen die Entwickler, alle Elemente in ein übergeordnetes Element zu packen. Dies scheint zwar kein ernstes Problem zu sein, aber es gibt Fälle, in denen Entwickler eine Komponente rendern möchten, ohne dass ein Container solche Elemente umgibt, sich aber damit begnügen müssen.

Mit Vue 3 wurde eine neue Funktion namens Fragmente eingeführt, und diese Funktion ermöglicht es Entwicklern, mehrere Elemente in ihrer Stammvorlagendatei zu haben. Mit Vue 2.x würde also eine Eingabefeld-Container-Komponente so aussehen;

 # inputComponent.vue <template> <div> <label :for="label">label</label> <input :type="type" : :name="label" /> </div> </template> <script> export default { name: "inputField", props: { label: { type: String, required: true, }, type: { type: String, required: true, }, }, }; </script> <style></style>

Hier haben wir eine einfache Formularelementkomponente, die zwei Requisiten akzeptiert, label und type , und der Vorlagenabschnitt dieser Komponente ist in ein div eingeschlossen. Dies ist nicht unbedingt ein Problem, aber wenn Sie möchten, dass sich die Beschriftung und das Eingabefeld direkt in Ihrem form befinden. Mit Vue 3 können Entwickler diese Komponente einfach so umschreiben, dass sie so aussieht;

 # inputComponent.vue <template class="testingss"> <label :for="label">{{ label }}</label> <input :type="type" : :name="label" /> </template>

Bei einem einzelnen Wurzelknoten werden Attribute immer dem Wurzelknoten zugeordnet und sie werden auch als Non-Prop-Attribute bezeichnet . Sie sind Ereignisse oder Attribute, die an eine Komponente übergeben werden, für die keine entsprechenden Eigenschaften in props oder emits definiert sind. Beispiele für solche Attribute sind class und id . Es ist jedoch erforderlich, explizit zu definieren, welchem ​​der Elemente in einer Multi-Root-Knotenkomponente zugeordnet werden soll.

Folgendes bedeutet die Verwendung von inputComponent.vue von oben;

  1. Beim Hinzufügen von class zu dieser Komponente in der übergeordneten Komponente muss angegeben werden, welcher Komponente diese class zugeordnet werden soll, sonst hat das Attribut keine Wirkung.
 <template> <div class="home"> <div> <input-component class="awesome__class" label="name" type="text" ></input-component> </div> </div> </template> <style> .awesome__class { border: 1px solid red; } </style>

Wenn Sie so etwas tun, ohne zu definieren, wo die Attribute zugeordnet werden sollen, erhalten Sie diese Warnung in Ihrer Konsole;

Fehlermeldung im Terminal, wenn Attribute nicht verteilt werden
Fehlermeldung im Terminal, wenn Attribute nicht verteilt werden (Große Vorschau)

Und die border hat keine Auswirkung auf die Komponente;

Komponente ohne Attributverteilung
Bauteil ohne Attributverteilung (Große Vorschau)
  1. Um dies zu beheben, fügen Sie dem Element, an das solche Attribute verteilt werden sollen, ein v-bind="$attrs" ;
 <template> <label :for="label" v-bind="$attrs">{{ label }}</label> <input :type="type" : :name="label" /> </template>

Hier teilen wir Vue mit, dass die Attribute an das label -Element verteilt werden sollen, was bedeutet, dass die awesome__class darauf angewendet werden soll. Wenn wir nun unser Element im Browser untersuchen, sehen wir, dass die Klasse jetzt zum label hinzugefügt wurde und daher jetzt ein Rahmen um das Label verläuft.

Komponente mit Attributverteilung
Bauteil mit Attributverteilung (Große Vorschau)

Globale API

Es war nicht ungewöhnlich, Vue.component oder Vue.use in der main.js -Datei einer Vue-Anwendung zu sehen. Diese Arten von Methoden sind als globale APIs bekannt und es gibt eine ganze Reihe davon in Vue 2.x. Eine der Herausforderungen dieser Methode besteht darin, dass es unmöglich ist, bestimmte Funktionen auf eine Instanz Ihrer App zu isolieren (wenn Sie mehr als eine Instanz in Ihrer App haben), ohne dass dies Auswirkungen auf andere Apps hat, da sie alle auf Vue gemountet sind. Das ist was ich meine;

 Vue.directive('focus', { inserted: el => el.focus() }) Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })

Für den obigen Code ist es unmöglich zu sagen, dass die Vue-Direktive mit app1 und das Mixin mit app2 , aber stattdessen sind beide in den beiden Apps verfügbar.

Vue 3 wird mit einer neuen globalen API geliefert, um diese Art von Problem mit der Einführung von createApp zu beheben. Diese Methode gibt eine neue Instanz einer Vue-App zurück. Eine App-Instanz macht eine Teilmenge der aktuellen globalen APIs verfügbar. Damit werden alle APIs (Komponente, Mixin, Anweisung, Verwendung usw.), die Vue von Vue 2.x mutieren, jetzt in einzelne App-Instanzen verschoben, und jetzt kann jede Instanz Ihrer Vue-App Funktionen haben, die einzigartig sind sie, ohne andere vorhandene Apps zu beeinträchtigen.

Jetzt kann der obige Code umgeschrieben werden als;

 const app1 = createApp({}) const app2 = createApp({}) app1.directive('focus', { inserted: el => el.focus() }) app2.mixin({ /* ... */ })

Es ist jedoch möglich, Funktionalitäten zu erstellen, die Sie für alle Ihre Apps freigeben möchten, und dies kann mithilfe einer Factory-Funktion erfolgen.

Ereignis-API

Eine der häufigsten Methoden, die Entwickler zum Übergeben von Daten zwischen Komponenten gewählt haben, die keine Eltern-Kind-Beziehung haben, außer der Verwendung des Vuex Store, ist die Verwendung von Event Bus. Einer der Gründe, warum diese Methode üblich ist, ist, wie einfach es ist, damit anzufangen;

 # eventBus.js const eventBus = new Vue() export default eventBus;

Danach wäre der nächste Schritt, diese Datei in main.js zu importieren, um sie global in unserer App verfügbar zu machen oder sie in Dateien zu importieren, die Sie benötigen.

 # main.js import eventBus from 'eventBus' Vue.prototype.$eventBus = eventBus

Jetzt können Sie Ereignisse ausgeben und auf solche ausgegebenen Ereignisse lauschen;

 this.$eventBus.$on('say-hello', alertMe) this.$eventBus.$emit('pass-message', 'Event Bus says Hi')

Es gibt eine Menge Vue-Codebasis, die mit Code wie diesem gefüllt ist. Mit Vue 3 wäre dies jedoch unmöglich, da $on , $off und $once alle entfernt wurden, aber $emit immer noch verfügbar ist, da es erforderlich ist, dass untergeordnete Komponenten Ereignisse an ihre übergeordneten Komponenten ausgeben. Eine Alternative dazu wäre die Verwendung provide / inject oder einer der empfohlenen Bibliotheken von Drittanbietern.

Fazit

In diesem Artikel haben wir behandelt, wie Sie mithilfe des provide / inject Daten von einer übergeordneten Komponente an eine tief verschachtelte untergeordnete Komponente weitergeben können. Wir haben uns auch angesehen, wie wir Komponenten von einem Punkt in unserer App zu einem anderen neu positionieren und übertragen können. Eine andere Sache, die wir uns angesehen haben, ist die Multi-Root-Node-Komponente und wie wir sicherstellen können, dass wir Attribute verteilen, damit sie richtig funktionieren. Schließlich haben wir auch die Änderungen an der Events-API und der globalen API behandelt.

Weitere Ressourcen

  • „JavaScript Factory-Funktionen mit ES6+“, Eric Elliott, Medium
  • „Verwenden von Event Bus zum Teilen von Requisiten zwischen Vue-Komponenten“, Kingsley Silas, CSS-Tricks
  • Using Multiple Teleports on the same target, Vue.js Docs
  • Nicht-Prop-Attribute, Vue.js-Dokumentation
  • Arbeiten mit Reaktivität, Vue.js Docs
  • teleport , Vue.js Docs
  • Fragmente, Vue.js-Dokumente
  • 2.x-Syntax, Vue.js-Dokumentation