→ Vue.js für Anfänger Lektion 1: Vue-Instanz
→ Vue.js für Anfänger, Lektion 2: Attributbindung
→ Vue.js für Anfänger, Lektion 3: Bedingtes Rendern
→ Vue.js für Anfänger, Lektion 4: Rendern von Listen
→ Vue .js für Anfänger Lektion 5: Ereignisverarbeitung
→ Vue.js Anfänger Lektion 6: Binden von Klassen und Stilen
→ Vue.js Anfänger Lektion 7: berechnete Eigenschaften
→ Vue.js Anfänger Lektion 8: Komponenten
→ Vue. js für Anfänger Lektion 9: Benutzerdefinierte Ereignisse
→ Vue.js für Anfänger Lektion 10: Formulare
Der Zweck der Lektion
Wir möchten Registerkarten auf der Anwendungsseite haben, von denen eine den Besuchern ermöglicht, Bewertungen zu Produkten zu schreiben, und die andere es ihnen ermöglicht, vorhandene Bewertungen anzuzeigen.
Anfangscode
So sieht der Inhalt der Datei in dieser Arbeitsphase aus
index.html:
<div id="app">
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product :premium="premium" @add-to-cart="updateCart"></product>
</div>
Darin
main.jsbefindet sich der folgende Code:
Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<p>Shipping: {{ shipping }}</p>
<ul>
<li v-for="(detail, index) in details" :key="index">{{ detail }}</li>
</ul>
<div
class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)"
></div>
<button
@click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
</div>
<div>
<h2><font color="#3AC1EF">Reviews</font></h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<product-review @review-submitted="addReview"></product-review>
</div>
`,
data() {
return {
product: 'Socks',
brand: 'Vue Mastery',
selectedVariant: 0,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg',
variantQuantity: 10
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg',
variantQuantity: 0
}
],
reviews: []
}
},
methods: {
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);
},
updateProduct(index) {
this.selectedVariant = index;
},
addReview(productReview) {
this.reviews.push(productReview)
}
},
computed: {
title() {
return this.brand + ' ' + this.product;
},
image() {
return this.variants[this.selectedVariant].variantImage;
},
inStock() {
return this.variants[this.selectedVariant].variantQuantity;
},
shipping() {
if (this.premium) {
return "Free";
} else {
return 2.99
}
}
}
})
Vue.component('product-review', {
template: `
<form class="review-form" @submit.prevent="onSubmit">
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<p>
<label for="name">Name:</label>
<input id="name" v-model="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: null,
errors: []
}
},
methods: {
onSubmit() {
if(this.name && this.review && this.rating) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
} else {
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id);
}
}
})
So sieht die Anwendung jetzt aus.

Bewerbungsseite
Aufgabe
Derzeit werden die Bewertungen und das Formular, mit dem Bewertungen eingereicht werden, auf der Seite nebeneinander angezeigt. Dies ist eine ziemlich funktionierende Struktur. Es wird jedoch erwartet, dass im Laufe der Zeit immer mehr Bewertungen auf der Seite erscheinen. Dies bedeutet, dass Benutzer bequemer mit einer Seite interagieren können, auf der nach ihrer Wahl entweder ein Formular oder eine Liste von Bewertungen angezeigt wird.
Die Lösung des Problems
Um unser Problem zu lösen, können wir der Seite ein System von Registerkarten hinzufügen. Einer von ihnen mit einem Titel
Reviewszeigt Rezensionen an. Die zweite mit einem Titel Make a Reviewzeigt ein Formular zum Einreichen von Bewertungen an.
Erstellen einer Komponente, die das Registerkartensystem implementiert
Beginnen wir mit der Erstellung einer Komponente
product-tabs. Es wird am unteren Rand der visuellen Darstellung der Komponente angezeigt product. Im Laufe der Zeit wird der Code ersetzt, der derzeit zum Anzeigen der Liste der Überprüfungen und Formulare auf der Seite verwendet wird.
Vue.component('product-tabs', {
template: `
<div>
<span class="tab" v-for="(tab, index) in tabs" :key="index">{{ tab }}</span>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review']
}
}
})
Im Moment ist dies nur eine leere Komponente, die wir bald fertigstellen werden. Lassen Sie uns zunächst kurz diskutieren, was in diesem Code dargestellt wird.
Die Komponentendaten enthalten ein Array
tabsmit den Zeichenfolgen, die wir als Registerkartenüberschriften verwenden. Die Komponentenvorlage verwendet eine Konstruktion v-for, tabsdie ein Element erstellt, <span>das die entsprechende Zeichenfolge für jedes Arrayelement enthält . Was diese Komponente in dieser Phase der Arbeit daran bildet, sieht wie folgt aus.

Die Produkt-Registerkarten-Komponente in der Anfangsphase der Arbeit daran
. Um unsere Ziele zu erreichen, müssen wir wissen, welche der Registerkarten aktiv ist. Fügen wir daher den Komponentendaten eine Eigenschaft hinzu
selectedTab. Wir werden den Wert dieser Eigenschaft mithilfe eines Ereignishandlers dynamisch festlegen, der auf Klicks auf die Tabulatortitel reagiert:
@click="selectedTab = tab"
Die Eigenschaft besteht aus Zeilen, die den Titeln der Registerkarten entsprechen.
Das heißt, wenn der Benutzer auf die Registerkarte klickt
Reviews, wird selectedTabeine Zeichenfolge geschrieben Reviews. Wenn Sie auf die Registerkarte klicken Make a Review, wird die selectedTabZeile eingefügt Make a Review.
So sieht der vollständige Komponentencode jetzt aus.
Vue.component('product-tabs', {
template: `
<div>
<ul>
<span class="tab"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review'],
selectedTab: 'Reviews' // @click
}
}
})
Binden einer Klasse an eine aktive Registerkarte
Ein Benutzer, der mit einer Schnittstelle arbeitet, die Registerkarten verwendet, muss wissen, welche Registerkarte aktiv ist. Sie können einen ähnlichen Mechanismus mithilfe der Klassenbindung an Elemente implementieren ,
<span>die zum Anzeigen von Registerkartennamen verwendet werden:
:class="{ activeTab: selectedTab === tab }"
Hier ist die CSS-Datei, die den Stil der hier verwendeten Klasse definiert
activeTab. So sieht dieser Stil aus:
.activeTab {
color: #16C0B0;
text-decoration: underline;
}
Und hier ist der Klassenstil
tab:
.tab {
margin-left: 20px;
cursor: pointer;
}
Wenn wir die obige Konstruktion in einfacher Sprache erklären, stellt sich heraus, dass der für die Klasse angegebene Stil auf die Registerkarte angewendet wird,
activeTabfalls selectedTabgleich tab. Da der selectedTabName der Registerkarte, auf die der Benutzer gerade geklickt hat, geschrieben ist, wird der Stil .activeTabspeziell auf die aktive Registerkarte angewendet.
Mit anderen Worten, wenn der Benutzer auf die erste Registerkarte klickt,
tabwird in Reviewsdasselbe geschrieben selectedTab. Infolgedessen wird der Stil auf die erste Registerkarte angewendet .activeTab.
Jetzt sehen die Tab-Titel auf der Seite wie folgt aus.

Der hervorgehobene Titel der aktiven Registerkarte Es
sieht so aus, als ob zu diesem Zeitpunkt alles wie erwartet funktioniert, sodass wir weitermachen können.
Arbeiten an der Komponentenvorlage
Nachdem wir dem Benutzer mitteilen können, welche Registerkarte aktiv ist, können wir weiter an der Komponente arbeiten. Wir sprechen nämlich über die Fertigstellung der Vorlage und beschreiben, was genau auf der Seite angezeigt wird, wenn jede der Registerkarten aktiviert wird.
Überlegen wir uns, was dem Benutzer angezeigt werden soll, wenn er auf die Registerkarte klickt
Reviews. Dies sind natürlich Produktbewertungen. Deshalb werden wir den Code bewegen auf Bewertungen aus der Komponentenvorlage auf die Komponente Anzeige productVorlage product-tabs, platzieren Sie diesen Code unter der Konstruktion verwendet , um die Registerkarte Header anzuzeigen. So sieht die Komponentenvorlage jetzt aus product-tabs:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
</div>
`
Beachten Sie, dass wir das Tag entfernt haben,
<h2><font color="#3AC1EF">da wir den Titel nicht mehr Reviewsüber der Liste der Bewertungen anzeigen müssen . Anstelle dieses Titels wird der Titel der entsprechenden Registerkarte angezeigt.
Das Verschieben des Vorlagencodes allein reicht jedoch nicht aus, um Feedback zu geben. Das Array,
reviewsdessen Daten zum Anzeigen von Bewertungen verwendet werden, wird als Teil der Komponentendaten gespeichert product. Wir müssen dieses Array product-tabsmithilfe des Komponenten- Requisitenmechanismus an die Komponente übergeben . Fügen wir dem Objekt product-tabsFolgendes mit Optionen hinzu, die während der Erstellung verwendet werden :
props: {
reviews: {
type: Array,
required: false
}
}
Übergeben wir ein Array
reviewsvon Komponente productzu Komponente product-tabsmithilfe der productfolgenden Konstruktion in der Vorlage :
<product-tabs :reviews="reviews"></product-tabs>
Überlegen wir uns nun, was auf der Seite angezeigt werden muss, wenn der Benutzer auf den Titel der Registerkarte klickt
Make a Review. Dies ist natürlich ein Formular zum Einreichen von Feedback. Um das Projekt für die weitere Arbeit vorzubereiten, übertragen wir den Komponentenverbindungscode product-reviewvon der Komponentenvorlage productin die Vorlage product-tabs. Platzieren Sie den folgenden Code unter dem Element <div>, mit dem die Liste der Bewertungen angezeigt wird:
<div>
<product-review @review-submitted="addReview"></product-review>
</div>
Wenn Sie sich jetzt die Anwendungsseite ansehen, werden Sie feststellen, dass die Liste der Bewertungen und das Formular unter den Registerkartenüberschriften angezeigt werden.

Eine Zwischenstufe der Bearbeitung der Seite
In diesem Fall wirken sich Klicks auf die Überschriften, obwohl sie zu ihrer Auswahl führen, in keiner Weise auf andere Elemente der Seite aus. Wenn Sie versuchen, das Formular zu verwenden, stellt sich heraus, dass es nicht mehr normal funktioniert. All dies sind durchaus erwartete Konsequenzen der Änderungen, die wir an der Anwendung vorgenommen haben. Lassen Sie uns weiterarbeiten und unser Projekt in einen funktionierenden Zustand bringen.
Bedingte Anzeige von Seitenelementen
Nachdem wir die Hauptelemente der Komponentenvorlage vorbereitet haben
product-tabs, ist es Zeit, ein System zu erstellen, mit dem Sie verschiedene Seitenelemente anzeigen können, basierend auf dem Tabulatortitel, auf den der Benutzer geklickt hat.
Die Komponentendaten haben bereits eine Eigenschaft
selectedTab. Wir können es in einer Direktive verwenden, v-showum bedingt zu rendern, was zu jeder der Registerkarten gehört.
Daher
<div>können wir dem Tag, das den Code zum Generieren der Liste der Bewertungen enthält, die folgende Konstruktion hinzufügen:
v-show="selectedTab === 'Reviews'"
Dank ihr wird die Liste der Bewertungen angezeigt, wenn die Registerkarte aktiv ist
Reviews.
In ähnlicher Weise fügen wir dem Tag
<div>, das den Komponentenverbindungscode enthält, product-reviewFolgendes hinzu:
v-show="selectedTab === 'Make a Review'"
Dies führt dazu, dass das Formular nur angezeigt wird, wenn die Registerkarte aktiv ist
Make a Review.
So sieht die Komponentenvorlage jetzt aus
product-tabs:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div v-show="selectedTab === 'Reviews'">
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<div v-show="selectedTab === 'Make a Review'">
<product-review @review-submitted="addReview"></product-review>
</div>
</div>
`
Wenn Sie sich die Seite ansehen und auf die Registerkarten klicken, können Sie sicherstellen, dass der von uns erstellte Mechanismus ordnungsgemäß funktioniert.

Durch Klicken auf die Registerkarten werden einige Elemente
ausgeblendet und andere angezeigt . Das Senden von Feedback über ein Formular funktioniert immer noch nicht. Lassen Sie uns das Problem untersuchen und beheben.
Lösen des Problems mit dem Senden von Feedback
Wenn Sie sich jetzt die Konsole der Browser-Entwicklertools ansehen, wird eine Warnung angezeigt.

Konsolenwarnung
Offensichtlich kann das System die Methode nicht erkennen
addReview. Was ist mit ihm passiert?
Um diese Frage zu beantworten, denken wir daran, dass es sich
addReviewum eine Methode handelt, die in einer Komponente deklariert istproduct. Es sollte aufgerufen werden, wenn die Komponenteproduct-review(und dies ist eine untergeordnete Komponente der Komponenteproduct) ein Ereignis generiertreview-submitted:
<product-review @review-submitted="addReview"></product-review>
So funktionierte alles, bevor das obige Code-Snippet auf die Komponente übertragen wurde
product-tabs. Und jetzt ist eine Komponente producteine untergeordnete Komponente product-tabs, und product-reviewjetzt ist sie kein „Kind“, eine Komponente product, sondern ihr „Enkel“.
Unser Code ist jetzt so konzipiert, dass er
product-reviewmit der übergeordneten Komponente interagiert . Aber jetzt ist es keine Komponente mehr product. Infolgedessen stellt sich heraus, dass wir den Projektcode umgestalten müssen, damit das Formular ordnungsgemäß funktioniert.
Projektcode umgestalten
Um die Kommunikation von Enkelkomponenten mit ihren "Großeltern" sicherzustellen oder um die Kommunikation zwischen Komponenten derselben Ebene herzustellen, wird häufig ein Mechanismus verwendet, der als globaler Ereignisbus bezeichnet wird.
Der Global Event Bus ist ein Kommunikationskanal, über den Informationen zwischen Komponenten übertragen werden können. Tatsächlich handelt es sich nur um eine Vue-Instanz, die erstellt wird, ohne dass ein Objekt mit Optionen übergeben wird. Erstellen wir einen Ereignisbus:
var eventBus = new Vue()
Dieser Code wird in die oberste Ebene der Datei verschoben
main.js.
Möglicherweise fällt es Ihnen leichter, dieses Konzept zu verstehen, wenn Sie sich den Ereignisbus als Bus vorstellen. Die Passagiere sind Daten, die einige Komponenten an andere senden. In unserem Fall geht es darum, Informationen über Ereignisse, die von anderen Komponenten generiert wurden, an eine Komponente zu übertragen. Das heißt, unser "Bus" fährt von Komponente
product-reviewzu Komponente product, trägt Informationen darüber, dass das Formular gesendet wurde, und liefert die Formulardaten von product-reviewan product.
Jetzt in der Komponente
product-review, in der Methode onSubmit, gibt es eine Zeile wie folgt:
this.$emit('review-submitted', productReview)
Wir werden es durch Folgendes ersetzen und
eventBusstattdessen verwenden this:
eventBus.$emit('review-submitted', productReview)
Danach müssen Sie das
review-submittedKomponentenereignis nicht mehr abhören product-review. Daher ändern wir den Code dieser Komponente in der Komponentenvorlage product-tabswie folgt:
<product-review></product-review>
Die
productMethode kann jetzt aus der Komponente entfernt werden addReview. Stattdessen verwenden wir die folgende Konstruktion:
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
Wir werden weiter unten darüber sprechen, wie man es in einer Komponente verwendet, aber im Moment werden wir kurz beschreiben, was darin passiert. Dieses Konstrukt gibt an, dass Sie beim
eventBusGenerieren eines Ereignisses review-submitteddie in diesem Ereignis übergebenen Daten (dh - productReview) in das reviewsKomponentenarray einfügen müssen product. Tatsächlich ist dies sehr ähnlich zu dem, was bisher in einer Methode gemacht wurde addReview, die wir nicht mehr brauchen. Beachten Sie, dass das obige Codefragment eine Pfeilfunktion verwendet. Dieser Moment verdient eine detailliertere Berichterstattung.
Gründe für die Verwendung einer Pfeilfunktion
Hier verwenden wir den Pfeil Funktion Syntax , die in ES6 eingeführt wurde. Der Punkt ist, dass der Kontext der Pfeilfunktion an den übergeordneten Kontext gebunden ist. Wenn wir in dieser Funktion ein Schlüsselwort verwenden
this, entspricht dies dem Schlüsselwort this, das der Entität entspricht, die die Pfeilfunktion enthält.
Dieser Code kann ohne Verwendung von Pfeilfunktionen neu geschrieben werden, aber dann müssen Sie die Bindung organisieren
this:
eventBus.$on('review-submitted', function (productReview) {
this.reviews.push(productReview)
}.bind(this))
Projekt abschließen
Wir haben unser Ziel fast erreicht. Alles, was noch getan werden muss, ist, einen Platz für den Code zu finden, der eine Antwort auf das Ereignis liefert
review-submitted. Eine productFunktion kann zu einem solchen Ort in einer Komponente werden mounted:
mounted() {
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
}
Was ist diese Funktion? Dies ist ein Lebenszyklus-Hook , der einmal aufgerufen wird, nachdem die Komponente im DOM bereitgestellt wurde. Nachdem die Komponente
productbereitgestellt wurde, wartet sie nun auf das Auftreten von Ereignissen review-submitted. Nachdem ein solches Ereignis generiert wurde, wird das, was in diesem Ereignis übergeben wird, zu den Komponentendaten hinzugefügt, d productReview. H.
Wenn Sie jetzt versuchen, mithilfe des Formulars eine Bewertung zum Produkt abzugeben, wird diese Bewertung dort angezeigt, wo sie sein sollte.

Form funktioniert wie es sollte
Der Ereignisbus ist nicht die beste Lösung für die Kommunikation von Komponenten
Obwohl der Ereignisbus häufig verwendet wird und Sie ihn möglicherweise in verschiedenen Projekten finden, sollten Sie bedenken, dass dies bei weitem nicht die beste Lösung für das Problem des Verbindens von Anwendungskomponenten ist.
Wenn die Anwendung wächst, kann ein auf Vuex basierendes Statusverwaltungssystem sehr nützlich sein . Es ist ein Muster und eine Bibliothek zur Verwaltung des Anwendungsstatus.
Werkstatt
Fügen Sie dem Projekt Registerkarten
Shippingund hinzu Details, in denen die Kosten für die Lieferung von Einkäufen und Informationen zu Waren angezeigt werden.
- Hier ist eine Vorlage, mit der Sie dieses Problem lösen können.
- Hier ist die Lösung des Problems.
Ergebnis
Folgendes haben Sie in diesem Tutorial gelernt:
- Sie können Tools zum bedingten Rendern verwenden, um den Registerkartenmechanismus zu organisieren.
- , Vue, .
- — . . — Vuex.
Wir hoffen, dass Sie nach dem Besuch dieses Vue-Kurses gelernt haben, was Sie wollten, und bereit sind, viel mehr neue und interessante Dinge über dieses Framework zu lernen.
Wenn Sie diesen Kurs gerade abgeschlossen haben, teilen Sie uns bitte Ihre Eindrücke mit.
→ Vue.js für Anfänger Lektion 1: Vue-Instanz
→ Vue.js für Anfänger, Lektion 2: Attributbindung
→ Vue.js für Anfänger, Lektion 3: Bedingtes Rendern
→ Vue.js für Anfänger, Lektion 4: Rendern von Listen
→ Vue .js für Anfänger Lektion 5: Ereignisbehandlung
→ Vue.js für Anfänger, Lektion 6: Bindungsklassen und -stile
→ Vue.js für Anfänger, Lektion 7: Berechnete Eigenschaften
→Vue.js für Anfänger, Lektion 8: Komponenten
→ Vue.js für Anfänger, Lektion 9: Benutzerdefinierte Ereignisse
→ Vue.js für Anfänger, Lektion 10: Formulare
