Kapitel 4. Wechselwirkungen zwischen Komponenten
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
In Kapitel 3 haben wir uns eingehend mit der Komposition einer Komponente mit Lifecycle Hooks, berechneten Eigenschaften, Watchern, Methoden und anderen Funktionen beschäftigt. Außerdem haben wir gelernt, wie mächtig Slots sind und wie man mit Props externe Daten von anderen Komponenten erhält.
Auf dieser Grundlage zeigt dir dieses Kapitel, wie du die Interaktionen zwischen den Komponenten mithilfe von benutzerdefinierten Ereignissen und Provide/Inject-Mustern aufbaust. Außerdem wird die Teleport-API vorgestellt, mit der du Elemente im DOM-Baum verschieben kannst, ohne dass ihre Reihenfolge innerhalb einer Vue-Komponente verändert wird.
Verschachtelte Komponenten und Datenfluss in Vue
Vue-Komponenten können andere Vue-Komponenten in sich selbst verschachteln. Diese Funktion ist praktisch, um den Code in einem komplexen UI-Projekt in kleinere, überschaubare und wiederverwendbare Teile zu gliedern. Wir nennen verschachtelte Elemente Kindkomponenten und die Komponente, die sie enthält, ihre Elternkomponente .
Der Datenfluss in einer Vue-Anwendung ist standardmäßig unidirektional, was bedeutet, dass die übergeordnete Komponente Daten an die untergeordnete Komponente weitergeben kann, aber nicht umgekehrt. Die Elternkomponente kann Daten an die Kindkomponente weitergeben, indem sie props
verwendet (siehe "Erkunden der Options-API"), und die Kindkomponente kann Ereignisse an die Elternkomponente zurücksenden, indem sie benutzerdefinierte Ereignisse emits
verwendet. Abbildung 4-1 veranschaulicht den Datenfluss zwischen den Komponenten .
Übergabe von Funktionen als Requisiten
Im Gegensatz zu anderen Frameworks ist es in Vue nicht möglich, eine Funktion als Prop an die Child-Komponente zu übergeben. Stattdessen kannst du die Funktion als benutzerdefinierten Ereignis-Emitter binden (siehe "Kommunikation zwischen Komponenten mitbenutzerdefinierten Ereignissen") .
Verwendung von Requisiten zur Weitergabe von Daten an untergeordnete Komponenten
In Form eines Objekts oder Arrays enthält das Feld props
einer Vue-Komponente alle verfügbaren Dateneigenschaften, die die Komponente von ihrem Elternteil erhalten kann. Jede Eigenschaft von props
ist eine Eigenschaft der Zielkomponente. Um Daten von der übergeordneten Komponente zu empfangen, musst du das Feld props
im Optionsobjekt der Komponente deklarieren, wie in Beispiel 4-1 gezeigt.
Beispiel 4-1. Definieren von Requisiten in einer Komponente
export
default
{
name
:
'ChildComponent'
,
props
:
{
name
:
String
}
}
In Beispiel 4-1 akzeptiert die Komponente ChildComponent
einen name
Prop vom Typ String
. Die übergeordnete Komponente kann dann Daten an die untergeordnete Komponente weitergeben, indem sie diese name
Prop benutzt, wie in Beispiel 4-2 gezeigt.
Beispiel 4-2. Übergabe statischer Daten als Requisiten an eine untergeordnete Komponente
<
template
>
<
ChildComponent
name
=
"Red Sweater"
/>
</
template
>
<
script
lang
=
"ts"
>
import
ChildComponent
from
'./ChildComponent.vue'
export
default
{
name
:
'ParentComponent'
,
components
:
{
ChildComponent
},
}
</
script
>
Die ChildComponent
erhält im vorherigen Beispiel einen statischen "Red Sweater" als name
Wert. Wenn du eine dynamische Datenvariable an name
übergeben und binden möchtest, z. B. das erste Element in der Liste children
, kannst du das Attribut v-bind
verwenden, das mit :
bezeichnet wird, wie in Beispiel 4-3 gezeigt.
Beispiel 4-3. Übergabe dynamischer Variablen als Requisiten an eine untergeordnete Komponente
<
template
>
<
ChildComponent
:name
=
"children[0]"
/>
</
template
>
<
script
lang
=
"ts"
>
import
ChildComponent
from
'./ChildComponent.vue'
export
default
{
//...
data
()
{
return
{
children
:
[
'Red Sweater'
,
'Blue T-Shirt'
,
'Green Hat'
]
}
}
}
</
script
>
Die Ausgabe des vorherigen Codes ist die gleiche wie die Übergabe eines statischen Strings, Red Sweater
, an die name
prop.
Hinweis
Wenn die Requisite name
nicht vom Typ String
ist, musst du trotzdem dasv-bind
Attribut (oder :
) verwenden, um statische Daten an die untergeordnete Komponente zu übergeben, z. B. :name="true"
für den Typ Boolean
oder :name="["hello", "world"]"
für den Typ Array
.
Wenn sich in Beispiel 4-3 der Wert von children[0]
ändert, aktualisiert Vue auch die name
Prop in ChildComponent
und die untergeordnete Komponente rendert ihren Inhalt bei Bedarf neu.
Wenn du mehr als eine Requisite in der untergeordneten Komponente hast, kannst du genauso vorgehen und die Daten an die entsprechende Requisite übergeben. Um zum Beispiel name
und price
eines Produkts an die Komponente ProductComp
zu übergeben, kannst du Folgendes tun(Beispiel 4-4).
Beispiel 4-4. Übergabe mehrerer Requisiten an eine untergeordnete Komponente
/** components/ProductList.vue */<
template
>
<
ProductComp
:name
=
"product.name"
:price
=
"product.price"
/>
</
template
>
<
script
lang
=
"ts"
>
import
ProductComp
from
'./ProductComp.vue'
export
default
{
name
:
'ProductList'
,
components
:
{
ProductComp
},
data
()
{
return
{
product
:
{
name
:
'Red Sweater'
,
price
:
19.99
}
}
}
}
</
script
>
Und wir können die Komponente ProductComp
wie in Beispiel 4-5 definieren.
Beispiel 4-5. Definieren mehrerer Requisiten in ProductComp
<
template
>
<
div
>
<
p
>
Product: {{ name }}</
p
>
<
p
>
Price: {{ price }}</
p
>
</
div
>
</
template
>
<
script
lang
=
"ts"
>
export
default
{
name
:
'ProductComp'
,
props
:
{
name
:
String
,
price
:
Number
}
}
</
script
>
Die Ausgabe sieht dann wie folgt aus:
Product: Red Sweater Price: 19.99
Alternativ kannst du v-bind
(nicht :
) verwenden, um das gesamte Objekt user
zu übergeben und seine Eigenschaften an die Requisiten der jeweiligen untergeordneten Komponente zu binden:
<
template
>
<
ProductComp
v-bind
=
"product"
/>
</
template
>
Beachte, dass nur die untergeordnete Komponente die entsprechenden deklarierten Requisiten erhält. Wenn du also in der übergeordneten Komponente ein weiteres Feld, product.description
, hast, kann die untergeordnete Komponente nicht darauf zugreifen.
Hinweis
Eine andere Möglichkeit, die Komponente props
zu deklarieren, ist die Verwendung eines Arrays von Strings, von denen jeder den Namen der akzeptierten Requisite darstellt, z. B. props: ["name", "price"]
. Dieser Ansatz ist praktisch, wenn du schnell einen Prototyp für eine Komponente erstellen willst. Ich empfehle dir jedoch dringend, die Objektform von props
zu verwenden und alle Requisiten mit Typen zu deklarieren, um die Lesbarkeit des Codes zu verbessern und Fehler zu vermeiden.
Wir haben gelernt, wie man Requisiten mit Typen deklariert, aber wie validieren wir die Daten, die an die Requisiten des Kindes übergeben werden, wenn sie benötigt werden? Wie können wir einen Fallback-Wert für eine Requisite festlegen, wenn kein Wert übergeben wird? Das werden wir als Nächstes herausfinden.
Deklaration von Prop-Typen mit Validierung und Standardwerten
In Beispiel 4-1 haben wir die Requisite name
als Typ String
deklariert. Vue gibt eine Warnung aus, wenn die übergeordnete Komponente während der Laufzeit einen Nicht-String-Wert an die Requisite name
übergibt. Um jedoch in den Genuss der Typüberprüfung von Vue zu kommen, sollten wir die vollständige Deklarationssyntax verwenden:
{
type
:
String
|
Number
|
Boolean
|
Array
|
Object
|
Date
|
Function
|
Symbol
,
default
?:
any
,
required
?:
boolean
,
validator
?:
(
value
:
any
)
=>
boolean
}
In dem:
-
type
ist der Typ der Requisite. Er kann eine Konstruktorfunktion (oder eine benutzerdefinierte Klasse) oder einer der eingebauten Typen sein. -
default
ist der Standardwert des Prop, wenn kein Wert übergeben wird. Für die TypenObject
,Function
, undArray
muss der Standardwert eine Funktion sein, die den Anfangswert zurückgibt. -
required
ist ein boolescher Wert, der angibt, ob die Eigenschaft obligatorisch ist. Wennrequired
true
ist, muss die übergeordnete Komponente einen Wert an die Requisite übergeben. Standardmäßig sind alle Requisiten optional. -
validator
ist eine Funktion, die den an prop übergebenen Wert validiert, hauptsächlich zur Fehlersuche bei der Entwicklung.
Wir können den name
Prop genauer definieren und einen Standardwert angeben, wie in Beispiel 4-6 gezeigt.
Beispiel 4-6. Definieren von prop als String mit einem Standardwert
export
default
{
name
:
'ChildComponent'
,
props
:
{
name
:
{
type
:
String
,
default
:
'Child component'
}
}
}
Wenn die übergeordnete Komponente keinen Wert angibt, greift die untergeordnete Komponente auf den Standardwert "Child component" für die name
prop zurück.
Wir können auch name
als obligatorische Eigenschaft für die Kindkomponente festlegen und einen Validator für die empfangenen Daten hinzufügen, wie in Beispiel 4-7 gezeigt.
Beispiel 4-7. Name als erforderlich mit einem Prop-Validator definieren
export
default
{
name
:
'ChildComponent'
,
props
:
{
name
:
{
type
:
String
,
required
:
true
,
validator
:
value
=>
value
!==
"Child component"
}
}
}
Wenn in diesem Szenario die Elternkomponente keinen Wert an die name
prop übergibt oder der angegebene Wert mit der Kindkomponente übereinstimmt, gibt Vue im Entwicklungsmodus eine Warnung aus(Abbildung 4-2) .
Hinweis
Für das Feld default
ist der Typ Function
eine Funktion, die den Anfangswert der Requisite zurückgibt. Du kannst ihn nicht verwenden, um Daten an die übergeordnete Komponente zurückzugeben oder um Datenänderungen auf der übergeordneten Ebene auszulösen.
Zusätzlich zu den eingebauten Typen und der Validierung, die Vue bietet, kannst du einen JavaScript Class
oder einen Funktionskonstruktor und TypeScript kombinieren, um deinen eigenen Prop-Typ zu erstellen. Ich werde sie im nächsten Abschnitt behandeln.
Deklarieren von Requisiten mit benutzerdefinierter Typüberprüfung
Die Verwendung von primitiven Typen wie Array
, String
oder Object
eignet sich für den grundlegenden Anwendungsfall. Wenn deine Anwendung jedoch wächst, können primitive Typen zu generisch sein, um den Typ deiner Komponente sicher zu halten. Nimm eine PizzaComponent
mit dem folgenden Template-Code:
<
template
>
<
header
>
Title: {{ pizza.title }}</
header
>
<
div
class
=
"pizza--details-wrapper"
>
<
img
:src
=
"pizza.image"
:alt
=
"pizza.title"
width
=
"300"
/>
<
p
>
Description: {{ pizza.description }}</
p
>
<
div
class
=
"pizza--inventory"
>
<
div
class
=
"pizza--inventory-stock"
>
Quantity: {{pizza.quantity}}</
div
>
<
div
class
=
"pizza--inventory-price"
>
Price: {{pizza.price}}</
div
>
</
div
>
</
div
>
</
template
>
Diese Komponente akzeptiert einen obligatorischen pizza
prop, der ein Object
ist und einige Details über die pizza
enthält:
export
default
{
name
:
'PizzaComponent'
,
props
:
{
pizza
:
{
type
:
Object
,
required
:
true
}
}
}
Das ist ganz einfach. Indem wir jedoch pizza
als Typ Object
deklarieren, gehen wir davon aus, dass das Elternteil immer das passende Objekt mit den entsprechenden Feldern (title
, image
, description
, quantity
und price
) übergibt, die für die Darstellung von pizza
erforderlich sind.
Diese Annahme kann zu einem Problem führen. Da pizza
Daten des Typs Object
akzeptiert, kann jede Komponente, die PizzaComponent
verwendet, beliebige Objektdaten an die Requisite pizza
übergeben, ohne dass die eigentlichen Felder für eine pizza
benötigt werden, wie in Beispiel 4-8.
Beispiel 4-8. Verwendung der Pizza-Komponente mit falschen Daten
<
template
>
<
div
>
<
h2
>
Bad usage of Pizza component</
h2
>
<
pizza-component
:pizza
=
"{ name: 'Pinia', description: 'Hawaiian pizza' }"
/>
</
div
>
</
template
>
Der vorangehende Code führt zu einer fehlerhaften Darstellung der Benutzeroberfläche von PizzaComponent
, bei der nur description
verfügbar ist und die restlichen Felder leer sind (mit einem fehlerhaften Bild), wie in Abbildung 4-3 gezeigt.
TypeScript kann die Datentypübereinstimmung auch hier nicht erkennen, da es die Typüberprüfung anhand des deklarierten Typs von pizza
durchführt: dem generischen Object
. Ein weiteres potenzielles Problem ist, dass die Übergabe von pizza
im falschen Format der Nesteigenschaften die App zum Absturz bringen kann. Um solche Unfälle zu vermeiden, verwenden wir daher benutzerdefinierte Typendeklarationen.
Wir können die Klasse Pizza
definieren und die Eigenschaft pizza
vom Typ Pizza
deklarieren, wie in Beispiel 4-9 gezeigt.
Beispiel 4-9. Einen benutzerdefinierten Typ Pizza deklarieren
class
Pizza
{
title
:
string
;
description
:
string
;
image
:
string
;
quantity
:
number
;
price
:
number
;
constructor
(
title
:
string
,
description
:
string
,
image
:
string
,
quantity
:
number
,
price
:
number
)
{
this
.
title
=
title
this
.
description
=
description
this
.
image
=
image
this
.
quantity
=
quantity
this
.
price
=
price
}
}
export
default
{
name
:
'PizzaComponent'
,
props
:
{
pizza
:
{
type
:
Pizza
,
required
:
true
}
}
}
Alternativ kannst du auch interface
oder type
von TypeScript verwenden, um deinen benutzerdefinierten Typ anstelle von Class
zu definieren. In solchen Szenarien musst du jedoch den Typ PropType
aus dem Paket vue
mit der folgenden Syntax verwenden, um den deklarierten Typ auf die Zielprop abzubilden:
type
:
Object
as
PropType
<
Your
-
Custom
-
Type
>
Schreiben wir stattdessen die Klasse Pizza
als interface
um(Beispiel 4-10).
Beispiel 4-10. Deklarieren eines benutzerdefinierten Pizza-Typs mit der TypeScript-Schnittstellen-API
import
type
{
PropType
}
from
'vue'
interface
Pizza
{
title
:
string
;
description
:
string
;
image
:
string
;
quantity
:
number
;
price
:
number
;
}
export
default
{
name
:
'PizzaComponent'
,
props
:
{
pizza
:
{
type
:
Object
as
PropType
<
Pizza
>
,
required
:
true
}
}
}
Wenn du PizzaComponent
mit dem falschen Datenformat verwendest, wird TypeScript den Fehler erkennen und entsprechend hervorheben.
Hinweis
Vue führt die Typüberprüfung während der Laufzeit durch, während TypeScript die Typüberprüfung während der Kompilierungszeit durchführt. Daher ist es eine gute Praxis, sowohl die Typprüfung von Vue als auch die Typprüfung von TypeScript zu verwenden, um sicherzustellen, dass dein Code fehlerfrei ist .
Requisiten mit defineProps() und withDefaults() deklarieren
Wie wir in "Setup" gelernt haben , bietet Vue ab Vue 3.x eine <script setup>
Syntax für die Deklaration einer funktionalen Komponente ohne die klassische Options-API. Innerhalb dieses <script setup>
Blocks kannst du defineProps()
verwenden, um Props zu deklarieren, wie in Beispiel 4-11 gezeigt.
Beispiel 4-11. Requisitendeklaration mit defineProps()
und <script setup>
<
script
setup
>
import
{
defineProps
}
from
'vue'
const
props
=
defineProps
({
name
:
{
type
:
String
,
default
:
"Hello from the child component."
}
})
</
script
>
Dank TypeScript können wir auch den akzeptierten Typ für defineProps()
pro Komponente mit Typvalidierung zur Kompilierzeit deklarieren, wie in Beispiel 4-12 gezeigt.
Beispiel 4-12. Requisitendeklaration mit defineProps()
und TypeScript type
<
script
setup
>
import
{
defineProps
}
from
'vue'
type
ChildProps
=
{
name
?:
string
}
const
props
=
defineProps
<
ChildProps
>()
</
script
>
In diesem Fall müssen wir, um den Standardwert der Requisite message
zu deklarieren, den Aufruf defineProps()
mit withDefaults()
umschließen, wie in Beispiel 4-13.
Beispiel 4-13. Requisitendeklaration mit defineProps()
und withDefaults()
import
{
defineProps
,
withDefaults
}
from
'vue'
type
ChildProps
=
{
name
?:
string
}
const
props
=
withDefaults
(
defineProps
<
ChildProps
>
(),
{
name
:
'Hello from the child component.'
})
Verwendung von defineProps() mit TypeScript Type Checking
Bei der Verwendung von defineProps()
können wir die Laufzeit- und die Kompilierzeit-Typüberprüfung nicht kombinieren. Ich empfehle die Verwendung von defineProps()
für den Ansatz in Beispiel 4-11, um eine bessere Lesbarkeit und eine Kombination aus Vue- und TypeScript-Typenprüfung zu erreichen.
Wir haben gelernt, wie man Props für die Übergabe von Rohdaten in einer Vue-Komponente deklariert, mit Typüberprüfung und Validierung. Als Nächstes werden wir uns ansehen, wie man Funktionen als benutzerdefinierte Ereignisauslöser an eine untergeordnete Komponente weitergibt .
Kommunikation zwischen Komponenten mitbenutzerdefinierten Ereignissen
Vue behandelt Daten, die über Props an eine untergeordnete Komponente übergeben werden, als schreibgeschützte und rohe Daten. Der einseitige Datenfluss stellt sicher, dass die übergeordnete Komponente die einzige ist, die die Datenprops aktualisieren kann. Oft wollen wir eine bestimmte Datenreferenz aktualisieren und mit der übergeordneten Komponente synchronisieren. Dazu verwenden wir das Feld emits
in den Optionen der Komponente, um benutzerdefinierte Ereignisse zu deklarieren.
Nimm zum Beispiel eine To-Do-Liste oder die Komponente ToDoList
. Diese ToDoList
verwendetToDoItem
als untergeordnete Komponente, um eine Liste von Aufgaben mit dem Code in Beispiel 4-14 darzustellen.
Beispiel 4-14. ToDoList
Komponente
<
template
>
<
ul
style
=
"list-style: none;"
>
<
li
v-for
=
"task in tasks"
:key
=
"task.id"
>
<
ToDoItem
:task
=
"task"
/>
</
li
>
</
ul
>
</
template
>
<
script
lang
=
"ts"
>
import
{
defineComponent
}
from
'vue'
import
ToDoItem
from
'./ToDoItem.vue'
import
type
{
Task
}
from
'./ToDoItem'
export
default
defineComponent
({
name
:
'ToDoList'
,
components
:
{
ToDoItem
},
data
()
{
return
{
tasks
:
[
{
id
:
1
,
title
:
'Learn Vue'
,
completed
:
false
},
{
id
:
2
,
title
:
'Learn TypeScript'
,
completed
:
false
},
{
id
:
3
,
title
:
'Learn Vite'
,
completed
:
false
},
]
as
Task
[]
}
}
})
</
script
>
Und ToDoItem
ist eine Komponente, die ein task
Attribut erhält und ein input
als Kontrollkästchen darstellt, mit dem der Nutzer die Aufgabe als erledigt oder nicht erledigt markieren kann. Dieses Element input
erhält task.completed
als Anfangswert für das Attribut checked
. Sehen wir uns Beispiel 4-15 an.
Beispiel 4-15. ToDoItem
Komponente
<
template
>
<
div
>
<
input
type
=
"checkbox"
:checked
=
"task.completed"
/>
<
span
>
{{ task.title }}</
span
>
</
div
>
</
template
>
<
script
lang
=
"ts"
>
import
{
defineComponent
,
type
PropType
}
from
'vue'
export
interface
Task
{
id
:
number
;
title
:
string
;
completed
:
boolean
;
}
export
default
defineComponent
({
name
:
'ToDoItem'
,
props
:
{
task
:
{
type
:
Object
as
PropType
<
Task
>,
required
:
true
,
}
},
})
</
script
>
Wenn ein Benutzer dieses input
Kästchen anklickt, wollen wir ein Ereignis namens task-completed-toggle
auslösen, um die übergeordnete Komponente über den task.completed
Wert der jeweiligen Aufgabe zu informieren. Dazu deklarieren wir das Ereignis zunächst im Feld emits
der Optionen der Komponente(Beispiel 4-16).
Beispiel 4-16. ToDoItem
Komponente mit Emits
/** ToDoItem.vue */
export
default
defineComponent
({
//...
emits
:
[
'task-completed-toggle'
]
})
Dann erstellen wir eine neue Methode onTaskCompleted
, um das Ereignis task-completed-toggle
mit dem neuen Wert von task.completed
aus dem Kontrollkästchen und task.id
als Nutzlast des Ereignisses zu senden(Beispiel 4-17).
Beispiel 4-17. ToDoItem
Komponente mit einer Methode zur Ausgabe des Ereignisses task-completed-toggle
/** ToDoItem.vue */
export
default
defineComponent
({
//...
methods
:
{
onTaskCompleted
(
event
:
Event
)
{
this
.
$emit
(
"task-completed-toggle"
,
{
...
this
.
task
,
completed
:
(
event
.
target
as
HTMLInputElement
)
?
.
checked
,
});
},
}
})
Hinweis
Wir verwenden defineComponent
, um die Optionen der Komponente zu umschließen und eine TypeScript-freundliche Komponente zu erstellen. Die Verwendung von define
Component
ist für einfache Komponenten nicht erforderlich, aber du musst sie verwenden, um auf andere Dateneigenschaften von this
in den Methoden, Hooks oder berechneten Eigenschaften der Komponenten zuzugreifen. Andernfalls wird TypeScript einen Fehler auslösen.
Dann binden wir die Methode onTaskCompleted
an das Ereignis change
des Elements input
, wie in Beispiel 4-18 gezeigt.
Beispiel 4-18. Die aktualisierte Vorlage der KomponenteToDoItem
<
div
>
<
input
type
=
"checkbox"
:checked
=
"task.completed"
@
change
=
"onTaskCompleted"
/>
<
span
>
{{ task.title }}</
span
>
</
div
>
Jetzt können wir in der übergeordneten Komponente <ToDoList>
von ToDoItem
das Ereignis task-completed-toggle
mit der Notation @
an eine Methode binden, und zwar mit der Vorlage in Beispiel 4-19.
Beispiel 4-19. Die aktualisierte Vorlage der KomponenteToDoList
<
template
>
<
ul
style
=
"list-style: none;"
>
<
li
v-for
=
"task in tasks"
:key
=
"task.id"
>
<
ToDoItem
:task
=
"task"
@
task-completed-toggle
=
"onTaskCompleted"
/>
</
li
>
</
ul
>
</
template
>
Die Methode onTaskCompleted
in der übergeordneten Komponente <ToDoList>
erhält die Nutzlast des Ereignisses task-completed-toggle
und aktualisiert den Wert task.completed
der spezifischen Aufgabe im Array tasks
, wie in Beispiel 4-20.
Beispiel 4-20. Das Skript der KomponenteToDoList
mit einer Methode zur Behandlung des Ereignisses task-completed-toggle
//...
export
default
{
//...
methods
:
{
onTaskCompleted
(
payload
:
{
id
:
number
;
completed
:
boolean
})
{
const
index
=
this
.
tasks
.
findIndex
(
t
=>
t
.
id
===
payload
.
id
)
if
(
index
<
0
)
return
this
.
tasks
[
index
].
completed
=
payload
.
completed
}
}
}
Diese Codeblöcke werden die in Abbildung 4-4 gezeigte Seite darstellen.
Vue aktualisiert die zugehörigen Daten in ToDoList
und rendert entsprechend die entsprechende ToDoItem
Komponenteninstanz. Du kannst das Kontrollkästchen aktivieren, um ein To-Do als erledigt zu markieren. Abbildung 4-5 zeigt, dass wir das Ereignis der Komponente mithilfe der VueDevtools erkennen können.
Benutzerdefinierte Ereignisse mit defineEmits() definieren
Ähnlich wie bei der "Deklaration von Requisiten mit defineProps() und withDefaults()" kannst du innerhalb eines <script setup>
Codeblocks defineEmits()
verwenden, um eigene Ereignisse zu definieren. Die Funktion defineEmits()
akzeptiert denselben Eingabeparametertyp wie emits
:
const
emits
=
defineEmits
([
'component-event'
])
Sie gibt dann eine Funktionsinstanz zurück, die wir verwenden können, um ein bestimmtes Ereignis von der Komponente aufzurufen:
emits
(
'component-event'
,
[...
arguments
])
So können wir den Skriptabschnitt von ToDoItem
wie in Beispiel 4-21 schreiben.
Beispiel 4-21. ToDoItem
Komponente mit dem benutzerdefinierten Ereignis mit defineEmits()
<
script
lang
=
"ts"
setup
>
//...
const
props
=
defineProps
({
task
:
{
type
:
Object
as
PropType
<
Task
>,
required
:
true
,
}
});
const
emits
=
defineEmits
([
'task-completed-toggle'
])
const
onTaskCompleted
=
(
event
:
Event
)
=>
{
emits
(
"task-completed-toggle"
,
{
id
:
props
.
task
.
id
,
completed
:
(
event
.
target
as
HTMLInputElement
)
?
.
checked
,
});
}
</
script
>
Beachte, dass wir hier nicht defineComponent
verwenden müssen, da es keine this
Instanz innerhalb des <script setup>
Codeblocks gibt.
Für eine bessere Typüberprüfung kannst du für das Ereignis task-completed-toggle
eine Nur-Typ-Deklaration anstelle eines einzelnen Strings verwenden. Verbessern wir die emits
Deklaration in Beispiel 4-21 und verwenden den Typ EmitEvents
wie in Beispiel 4-22 gezeigt.
Beispiel 4-22. Benutzerdefiniertes Ereignis mit defineEmits()
und Nur-Typ-Deklaration
// Declare the emit type
type
EmitEvents
=
{
(
e
:
'task-completed-toggle'
,
task
:
Task
)
:
void
;
}
const
emits
=
defineEmits
<
EmitEvents
>
()
Auf diese Weise kannst du sicherstellen, dass du die richtige Methode an das deklarierte Ereignis bindest. Wie beim Ereignis task-complete-toggle
sollte jede Ereignisdeklaration demselben Muster folgen:
(e: 'component-event', [...arguments]): void
In der vorherigen Syntax ist e
der Name des Ereignisses, und arguments
sind alle Eingaben, die an den Ereignisauslöser übergeben werden. Im Fall des Ereignisses task-completed-toggle
ist das Argument des Emitters task
vom Typ Task
.
emits
ist eine leistungsstarke Funktion, mit der du die Zwei-Wege-Kommunikation zwischen einer Eltern- und einer Kindkomponente ermöglichen kannst, ohne den Datenflussmechanismus von Vue zu unterbrechen. props
und emits
sind jedoch nur von Vorteil, wenn du eine direkte Datenkommunikation wünschst.
Du musst einen anderen Ansatz verwenden, um Daten von einer Komponente an ihr Enkelkind oder ihren Nachkommen zu übergeben. Im nächsten Abschnitt werden wir sehen, wie du die APIs provide
und inject
verwendest, um Daten von einer übergeordneten Komponente an ihre untergeordnete oder Enkelkomponente zu übergeben.
Kommunikation zwischen Komponenten mitprovide/inject-Muster
Um die Datenkommunikation zwischen einer Vorgängerkomponente und ihren Nachfolgern herzustellen, ist die provide/inject
API eine sinnvolle Option. Das Feld provide
übergibt Daten von der Vorgängerkomponente, während inject
sicherstellt, dass Vue die bereitgestellten Daten in jeden Zielnachfolger injiziert.
Daten weitergeben mit provide
Das Optionsfeld der Komponente provide
akzeptiert zwei Formate: ein Datenobjekt oder eineFunktion.
provide
kann ein Objekt sein, das zu injizierende Daten enthält, wobei jede Eigenschaft einen Datentyp (Schlüssel, Wert) darstellt. Im folgenden Beispiel liefert ProductList
einen Datenwert, selectedIds
, mit dem Wert [1]
an alle seine Nachkommen(Beispiel 4-23).
Beispiel 4-23. Übergabe von selectedIds
mittels provide in der Komponente ProductList
export
default
{
name
:
'ProductList'
,
//...
provide
:
{
selectedIds
:
[
1
]
},
}
Ein weiterer Formattyp für provide
ist eine Funktion, die ein Objekt zurückgibt, das die Daten enthält, die für die Nachkommenschaft injiziert werden können. Ein Vorteil dieses Formattyps ist, dass wir auf die Instanz this
zugreifen und dynamische Daten oder eine Komponentenmethode auf die entsprechenden Felder des Rückgabeobjekts mappen können. Aus Beispiel 4-23 können wir das Feld provide
in eine Funktion umschreiben, wie in Beispiel 4-24 gezeigt.
Beispiel 4-24. Übergabe von selectedIds
mittels provide in der Komponente ProductList
als Funktion
export
default
{
//...
provide
()
{
return
{
selectedIds
:
[
1
]
}
},
//...
}
<
/script>
Hinweis
Im Gegensatz zu props
kannst du eine Funktion übergeben und sie über das Feld provide
vom Zielnachfolger auslösen lassen. Auf diese Weise können Daten an die übergeordnete Komponente zurückgeschickt werden. Vue betrachtet diesen Ansatz jedoch als Anti-Pattern und du solltest ihn mit Bedacht einsetzen.
An diesem Punkt übergibt unser ProductList
einige Datenwerte an seinen Nachkommen mitprovide
. Als Nächstes müssen wir die bereitgestellten Werte injizieren, um innerhalb eines Nachfolgers zu operieren.
Injizieren zum Empfangen von Daten verwenden
Wie props
kann auch das Feld inject
ein Array von Strings akzeptieren, die jeweils für den angegebenen Datenschlüssel (inject: [selectedId]
) oder ein Objekt darstellt.
Wenn du inject
als Objektfeld verwendest, ist jede seiner Eigenschaften ein Objekt, wobei der Schlüssel den lokalen Datenschlüssel darstellt, der innerhalb der Komponente verwendet wird, und die folgendenEigenschaften:
{ from?: string; default: any }
Hier ist from
optional, wenn der Eigenschaftsschlüssel derselbe ist wie der vom Vorgänger bereitgestellte Schlüssel. Nehmen wir zum Beispiel Beispiel Beispiel 4-23 mit selectedIds
als Daten, die von ProductList
an seine Nachkommen weitergegeben werden. Wir können ein ProductComp
berechnen, das die bereitgestellten Daten selectedIds
von ProductList
erhält und es in current
umbenennenSelectedIds
um es lokal zu verwenden, wie in Beispiel 4-25 gezeigt.
Beispiel 4-25. Injizieren von bereitgestellten Daten in ProductComp
<
script
lang
=
'ts'
>
export
default
{
//...
inject
:
{
currentSelectedIds
:
{
from
:
'selectedIds'
,
default
:
[]
},
},
}
</
script
>
In diesem Code nimmt Vue den Wert von selectedIds
und weist ihn einem lokalen Datenfeld, currentSelectedIds
, zu oder verwendet den Standardwert []
, wenn es keinen injizierten Wert gibt.
Wenn du im Abschnitt Komponenten auf der Registerkarte Vue in den Entwicklertools des Browsers ProductComp
aus dem Komponentenbaum auswählst (linke Seite), kannst du die Anzeige der Umbenennung für die injizierten Daten debuggen (rechte Seite), wie in Abbildung 4-6 gezeigt.
Hinweis
Die entsprechenden Hooks in der Composition API für provide/inject
sind provide()
und inject()
, bzw. .
Jetzt wissen wir, wie wir provide
und inject
verwenden können, um Daten zwischen Komponenten effizient und ohne Requisiten zu übergeben. Sehen wir uns an, wie wir einen bestimmten Inhaltsbereich eines Elements an einer anderen Stelle im DOM mit der Komponente <Teleport>
rendern können.
Teleport API
Aufgrund von Styling-Einschränkungen müssen wir oft eine Komponente implementieren, die Elemente enthält, die von Vue an einer anderen Stelle im DOM gerendert werden sollen, um den vollen visuellen Effekt zu erzielen. In solchen Fällen müssen wir diese Elemente in der Regel an die gewünschte Stelle "teleportieren", indem wir eine komplexe Lösung entwickeln, was sich negativ auf die Leistung auswirkt, Zeit kostet usw. Um diese "Teleport"-Herausforderung zu lösen, bietet Vue dieKomponente <Teleport>
.
Die Komponente <Teleport>
akzeptiert einen prop to
, der den Zielcontainer angibt, entweder den Abfrage-Selektor eines Elements oder das gewünschte HTML-Element. Angenommen, wir haben eine House
Komponente, die einen Ausschnitt des Himmels und der Wolken enthält, der von der Vue-Engine zu einem bestimmten #sky
DOM-Element teleportiert werden muss, wie in Beispiel 4-26.
Beispiel 4-26. Hauskomponente mit Teleport
<
template
>
<
div
>
This is a house</
div
>
<
Teleport
to
=
"#sky"
>
<
div
>
Sky and clouds</
div
>
</
Teleport
>
</
template
>
In unserem App.vue
fügen wir ein section
Element mit der Ziel-ID sky
oberhalb der House
Komponente ein, wie in Beispiel 4-27.
Beispiel 4-27. Template von App.vue
mit House
Komponente
<
template
>
<
section
id
=
"sky"
/>
<
section
class
=
"wrapper"
>
<
House
/>
</
section
>
</
template
>
Abbildung 4-7 zeigt die Codeausgaben.
Wenn du den DOM-Baum auf der Registerkarte "Elemente" in den Entwicklertools des Browsers inspizierst, erscheint "Himmel und Wolken" stattdessen als verschachtelt innerhalb von <section id="sky">
(Abbildung 4-8).
Du kannst auch das Verschieben des Inhalts innerhalb einer <Teleport>
Komponenteninstanz mit der booleschen Eigenschaft disabled
vorübergehend deaktivieren. Diese Komponente ist praktisch, wenn du die DOM-Baumstruktur beibehalten willst und Vue bei Bedarf nur den gewünschten Inhalt an die Zielposition verschieben soll. Ein alltäglicher Anwendungsfall für Teleport
ist ein Modal, das wir als Nächstes implementieren werden.
Beide Abschnitte unter einem Elternteil zusammenfassen
Die Zielkomponente für die Teleportation muss im DOM vorhanden sein, bevor <Teleport>
eingebunden wird. Wenn du in Beispiel 4-27 beide Instanzen von section
unter einem main
Element einbindest, funktioniert die Komponente <Teleport>
nicht wie erwartet. Siehe "Rendering-Problem mit Teleport" für weitere Details.
Ein Modal mit Teleport und dem <dialog>-Element implementieren
Ein Modal ist ein Dialogfenster, das oben auf dem Bildschirm erscheint und die Interaktion des Nutzers mit der Hauptseite blockiert. Der Nutzer muss mit dem Modal interagieren, um es zu schließen, und kehrt dann zur Hauptseite zurück.
Ein Modal ist sehr praktisch, um wichtige Benachrichtigungen anzuzeigen, die die volle Aufmerksamkeit des Nutzers erfordern und nur einmal erscheinen sollten.
Lass uns ein einfaches Modal entwerfen. Ähnlich wie ein Dialog, sollte ein Modal die folgenden Elemente enthalten(Abbildung 4-9):
-
Ein Hintergrund, der den gesamten Bildschirm bedeckt, auf dem das Modal erscheint und die Interaktionen des Nutzers mit der aktuellen Seite blockiert.
-
Ein modales Fenster, das den Inhalt des Modals enthält, einschließlich eines
header
mit einem Titel und einer Schaltfläche zum Schließen, einemmain
Inhaltsbereich und einemfooter
Bereich mit einer Standard-Schließschaltfläche. Diese drei Abschnitte sollten mit den Slots angepasst werden können.
Basierend auf dem vorangegangenen Entwurf implementieren wir eine Modal
Komponentenvorlage mit dem <dialog>
HTML-Element in Beispiel 4-28.
Beispiel 4-28. Modal
Komponente
<
template
>
<
dialog
:open
=
"open"
>
<
header
>
<
slot
name
=
"m-header"
>
<
h2
>
{{ title }}
<
/
h2
>
<
button
>
X
<
/
button
>
<
/
slot
>
<
/
header
>
<
main
>
<
slot
name
=
"m-main"
/
>
<
/
main
>
<
footer
>
<
slot
name
=
"m-footer"
>
<
button
>
Close
<
/
button
>
<
/
slot
>
<
/
footer
>
<
/
dialog
>
<
/
template
>
Im vorangegangenen Code verwenden wir drei Slot-Abschnitte, damit der Nutzer sie anpassen kann:
Außerdem binden wir das open
Attribut des <dialog>
Elements an eine lokale Datenreferenz open
zur Steuerung der Sichtbarkeit des Modals (sichtbar/unsichtbar). Darüber hinaus wird der title
prop als Standardtitel des Modals gerendert. Nun wollen wir die Optionen der Komponente Modal
implementieren, die zwei Requisiten erhalten: open
und title
, wie in Beispiel 4-29.
Beispiel 4-29. Hinzufügen von Requisiten zur Komponente Modal
<
script
lang
=
"ts"
>
import
{
defineComponent
}
from
'vue'
export
default
defineComponent
({
name
:
'Modal'
,
props
:
{
open
:
{
type
:
Boolean
,
default
:
false
,
},
title
:
{
type
:
String
,
default
:
'Dialog'
,
},
},
})
</
script
>
Wenn ein Nutzer auf die Schaltfläche zum Schließen des Modals oder auf die Schaltfläche "X" in der Kopfzeile klickt, sollte es sich selbst schließen. Da wir die Sichtbarkeit des Modals mit der Requisite open
steuern, müssen wir ein Ereignis closeDialog
mit dem neuen Wert von open
von der Komponente Modal
an das Elternteil senden. Wir deklarieren emits
und eine close
Methode, die das Zielereignis ausgibt, wie in Beispiel 4-30 .
Beispiel 4-30. Deklaration des Ereignisses closeDialog
für Modal
zum Aussenden
<
script
lang
=
"ts"
>
/** Modal.vue */
import
{
defineComponent
}
from
'vue'
export
default
defineComponent
(
{
name
:
'Modal'
,
//...
emits
:
[
"closeDialog"
]
,
methods
:
{
close
(
)
{
this
.
$emit
(
"closeDialog"
,
false
)
;
}
,
}
,
}
)
<
/
script
>
emits
mit einer Veranstaltung,closeDialog
close
Methode, die das EreigniscloseDialog
mit dem neuen Wert vonopen
ausgibt, alsfalse
Dann binden wir sie mit Hilfe der @
Notation an die entsprechenden Aktionselemente im <dialog>
Element, wie in Beispiel 4-31 gezeigt.
Beispiel 4-31. Ereignis-Listener auf Klick-Ereignisse binden
<
template
>
<
dialog
:open
=
"open"
>
<
header
>
<
slot
name
=
"m-header"
>
<
h2
>
{{ title }}
<
/
h2
>
<
button
@
click
=
"close"
>
X
<
/
button
>
<
/
slot
>
<
/
header
>
<
main
>
<
slot
name
=
"m-main"
/
>
<
/
main
>
<
footer
>
<
slot
name
=
"m-footer"
>
<
button
@
click
=
"close"
>
Close
<
/
button
>
<
/
slot
>
<
/
footer
>
<
/
dialog
>
<
/
template
>
@click
Ereignishandler für die Schaltfläche "X" in der Kopfzeile@click
Ereignishandler für die Standard-Schließschaltfläche in der Fußzeile
Als Nächstes müssen wir das Element dialog
mit einer Komponente <Teleport>
umhüllen, um es außerhalb des DOM-Baums der übergeordneten Komponente zu platzieren. Außerdem übergeben wir der Komponente den Prop to
<Teleport>
Komponente, um den Zielort festzulegen: ein HTML-Element mit der ID modal
. Zum Schluss binden wir die Requisite disabled
an den Wert open
der Komponente, um sicherzustellen, dass Vue nur den Inhalt der modalen Komponente an die gewünschte Stelle verschiebt, wenn sie sichtbar ist(Beispiel 4-32).
Beispiel 4-32. Verwendung der Komponente <Teleport>
<
template
>
<
teleport
to
=
"#modal"
:disabled
=
"!open"
>
<
dialog
ref
=
"dialog"
:open
=
"open"
>
<
header
>
<
slot
name
=
"m-header"
>
<
h2
>
{{ title }}
<
/
h2
>
<
button
@
click
=
"close"
>
X
<
/
button
>
<
/
slot
>
<
/
header
>
<
main
>
<
slot
name
=
"m-main"
/
>
<
/
main
>
<
footer
>
<
slot
name
=
"m-footer"
>
<
button
@
click
=
"close"
>
Close
<
/
button
>
<
/
slot
>
<
/
footer
>
<
/
dialog
>
<
/
teleport
>
<
/
template
>
<Teleport>
Komponenteto
Prop mit dem Zielort mit id-Selektormodal
disabled
Prop mit der Bedingung, dass deropen
Wert der Komponente falsch ist
Probieren wir nun unsere Modal
Komponente in einer WithModalComponent
aus, indem wir den folgenden Code aus Beispiel 4-33 in die WithModalComponent
einfügen.
Beispiel 4-33. Verwendung der modalen Komponente in WithModalComponent
<
template
>
<
h2
>
With Modal component</
h2
>
<
button
@
click
=
"openModal = true"
>
Open modal</
button
>
<
Modal
:open
=
"openModal"
title
=
"Hello World"
@
closeDialog
=
"toggleModal"
/>
</
template
>
<
script
lang
=
"ts"
>
import
{
defineComponent
}
from
"vue"
;
import
Modal
from
"./Modal.vue"
;
export
default
defineComponent
({
name
:
"WithModalComponent"
,
components
:
{
Modal
,
},
data
()
{
return
{
openModal
:
false
,
};
},
methods
:
{
toggleModal
(
newValue
:
boolean
)
{
this
.
openModal
=
newValue
;
},
},
});
</
script
>
Zum Schluss fügst du ein Element <div>
mit der ID modal
zum Element body
in der Datei index.html
hinzu:
<
body
>
<
div
id
=
"app"
>
<
/
div
>
<
div
id
=
"modal"
>
<
/
div
>
<
script
type
=
"module"
src
=
"/src/main.ts"
>
<
/
script
>
<
/
body
>
Auf diese Weise rendert Vue den Inhalt der Komponente Modal
auf diese div
mit der ID modal
, wenn der Prop open
auf true
(Abbildung 4-10) gesetzt wird.
Abbildung 4-11 zeigt, wie es auf dem Bildschirm aussieht:
Und wenn die Requisite open
false
ist, ist die Requisite div
mit der Id modal
leer(Abbildung 4-12) und das Modal ist auf dem Bildschirm unsichtbar(Abbildung 4-13).
Jetzt hast du eine funktionierende modale Komponente. Allerdings sieht das Modal nicht ganz so gut aus, wie wir es uns gewünscht haben. Wenn das Modal sichtbar ist, sollte sich ein dunkler Overlay über den Hauptinhalt der Seite legen. Beheben wir dieses Problem mit CSS-Stylings für den ::backdrop
-Selektor im <style>
-Abschnitt des Modalelements:
<
style
scoped
>
dialog
::backdrop
{
background-color
:
rgba
(
0
,
0
,
0
,
0
.
5
);
}
</style>
Das Aussehen des Hintergrunds des Modals wird dadurch jedoch nicht verändert. Das liegt daran, dass der Browser die ::backdrop
CSS-Selektorregeln nur dann auf das Dialogfeld anwendet, wenn wir das Dialogfeld mit der Methode dialog.showModal()
öffnen, und nicht, wenn wir das Attribut open
ändern. Um dieses Problem zu beheben, müssen wir die folgenden Änderungen in unserer Modal
Komponente vornehmen:
-
Füge einen direkten Verweis auf das Element
<dialog>
hinzu, indem du dem Attributref
den Wert "dialog" zuweist:<
dialog
:open
=
"open"
ref
=
"dialog"
>
<!--...-->
</
dialog
>
-
Löse
$refs.dialog.showModal()
oder$refs.dialog.close()
auf dem Elementdialog
aus, wenn sich die Requisiteopen
mitwatch
ändert:watch
:
{
open
(
newValue
)
{
const
element
=
this
.
$refs
.
dialog
as
HTMLDialogElement
;
if
(
newValue
)
{
element
.
showModal
();
}
else
{
element
.
close
();
}
},
},
-
Entferne die ursprüngliche Bindung für das Attribut
open
des Elements<dialog>
:<
dialog
ref
=
"dialog"
>
<!--...-->
</
dialog
>
-
Entferne die Verwendung des Attributs
disabled
in der Komponente<teleport>
:<
teleport
to
=
"#modal"
>
<!--...-->
</
teleport
>
Wenn du das Modal mit der eingebauten Methode showModal()
öffnest, fügt der Browser ein ::backdrop
Pseudo-Element zum eigentlichen <dialog>
Element im DOM hinzu. Wenn du den Inhalt des Elements dynamisch an die Zielposition verschiebst, wird diese Funktion deaktiviert und das Modal bleibt ohne den gewünschten Hintergrund.
Außerdem positionieren wir das Modal neu in der Mitte der Seite und über anderen Elementen, indem wir die folgenden CSS-Regeln zum dialog
-Selektor hinzufügen:
dialog
{
position
:
fixed
;
z-index
:
999
;
inset
-
block
-
start
:
30%
;
inset
-
inline
-
start
:
50%
;
width
:
300px
;
margin
-
inline
-
start
:
-150px
;
}
Wenn das Modal sichtbar ist, sieht die Ausgabe wie in Abbildung 4-14 dargestellt aus.
Wir haben gelernt, wie man eine wiederverwendbare Modal
Komponente mit Teleport
implementiert und haben verschiedene Anwendungsfälle mit den eingebauten <dialog>
Elementfunktionen erforscht. Wir haben auch gelernt, wie man den ::backdrop
CSS-Selektor verwendet, um den Hintergrund des Modals zu gestalten.
Wie du bemerkt hast, haben wir den Zielort div
für das Modal so festgelegt, dass er ein direktes Kind von body
ist, außerhalb des Vue-App-Eingabeelements <div id="app">
. Was passiert, wenn wir das Ziel des Modals div
innerhalb der Eingangskomponente App.vue
der Vue-Anwendung verschieben wollen? Das werden wir im nächsten Abschnitt herausfinden.
Rendering-Problem mit Teleport
Um das Problem mit der Verwendung von Teleport
zum Rendern des Modals innerhalb einer untergeordneten Komponente der Komponente App.vue
zu verstehen, verschieben wir zunächst <div id="modal"></div>
von index.html
nach App.vue
, nach der WithModalComponent
Instanz :
<
template
>
<
section
class
=
"wrapper"
>
<
WithModalComponent
/>
</
section
>
<
div
id
=
"modal"
></
div
>
</
template
>
Nachdem du deine Anwendung ausgeführt hast, kannst du sehen, dass der Browser das Modal nicht rendert, egal wie oft du auf die Schaltfläche Open modal
klickst. Und die Konsole zeigt den folgenden Fehler an:
Aufgrund des Vue-Mechanismus für die Rendering-Reihenfolge wartet das Elternteil, bis die Kinder gerendert sind, bevor es selbst gerendert wird. Die Kinder werden in der Reihenfolge gerendert, in der sie im Abschnitt template
des Elternteils erscheinen. In diesem Szenario wird das Element WithModalComponent
zuerst gerendert. Vue rendert also das Element <dialog>
und beginnt damit, den Inhalt der Komponente an die Zielposition zu verschieben, bevor ParentComponent
gerendert wird. Da ParentComponent
jedoch noch darauf wartet, dass WithModalComponent
sein Rendering beendet, existiert das Element <div id="modal">
noch nicht im DOM. Daher kann Vue die Zielposition nicht finden und die richtige Verschiebung nicht durchführen und das <dialog>
Element nicht innerhalb des <div id="modal">
Elements rendern, daher der Fehler.
Ein Workaround, um diese Einschränkung zu umgehen, ist, das Zielelement<div id="modal">
vor WithModalComponent
zu platzieren:
<
template
>
<
div
id
=
"modal"
></
div
>
<
section
class
=
"wrapper"
>
<
WithModalComponent
/>
</
section
>
</
template
>
Diese Lösung stellt sicher, dass das Ziel div
verfügbar ist, bevor Vue das Element Modal
rendert und den Inhalt verschiebt. Ein anderer Ansatz besteht darin, das disabled
Attribut zu verwenden, um das Verschieben des Inhalts für Modal
während des Renderns zu verschieben, bis der Nutzer auf die Schaltfläche Open modal
klickt. Beide Optionen haben Vor- und Nachteile, und du solltest dich für diejenige entscheiden, die deinen Bedürfnissen am besten entspricht.
Die gängigste Lösung ist, das Zielelement als direktes Kind des body
Elements einzufügen und es vom Vue-Rendering-Kontext zu isolieren.
Ein wesentlicher Vorteil der Verwendung von <Teleport>
ist die Erzielung eines maximalen visuellen Anzeigeeffekts (z. B. Vollbildmodus, modal, Seitenleiste usw.) bei gleichzeitiger Beibehaltung der Codehierarchiestruktur, Komponentenisolierung und Lesbarkeit .
Zusammenfassung
In diesem Kapitel wurde das Konzept der verschiedenen Ansätze für die Kommunikation von Komponenten mit Hilfe der eingebauten Vue-Funktionen wie props
, emits
und provide/inject
untersucht. Wir haben gelernt, wie wir diese Funktionen nutzen können, um Daten und Ereignisse zwischen Komponenten weiterzugeben, ohne den Datenflussmechanismus von Vue zu beeinträchtigen. Wir haben auch gelernt, wie man die Teleport-API verwendet, um ein Element außerhalb des DOM-Baums der übergeordneten Komponente zu rendern, während die Reihenfolge der Darstellung in der übergeordneten Komponente beibehalten wird <template>
. <Teleport>
ist von Vorteil für die Erstellung von Komponenten, die mit Ausrichtung auf das Hauptelement der Seite angezeigt werden müssen, wie z. B. Popups, Dialoge, Modals usw.
Im nächsten Kapitel werden wir mehr über die Composition API erfahren und wie man sie nutzt, um Vue-Komponenten zusammenzustellen.
Get Vue lernen now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.