Vue - Components: Unterschied zwischen den Versionen
| Zeile 35: | Zeile 35: | ||
== Quickstart - Component mit CLI == | == Quickstart - Component mit CLI == | ||
Wenn man mit Components arbeitet bietet sich auch die Arbeit mit dem [[Vue CLI]] an. In Kurzform die Schritte die Du benötigst um eine Vue App und Komponenten zu nutzen. | Wenn man mit Components arbeitet bietet sich auch die Arbeit mit dem [[Vue CLI]] an. In Kurzform die Schritte die Du benötigst um eine Vue App und Komponenten zu nutzen. | ||
| − | Komponente erstellen: | + | '''Komponente erstellen:''' |
* '''components/MyComponent.vue''' | * '''components/MyComponent.vue''' | ||
** <template> Abschnitt | ** <template> Abschnitt | ||
** <script>export default{}</script> Abschnitt | ** <script>export default{}</script> Abschnitt | ||
| − | Komponente in Main App registrieren | + | '''Komponente in Main App registrieren''' |
* '''App.vue''' erstellen | * '''App.vue''' erstellen | ||
** <template> und <script> wie oben erstellen | ** <template> und <script> wie oben erstellen | ||
| Zeile 49: | Zeile 49: | ||
** mit Funktion components in App Instanz registrieren | ** mit Funktion components in App Instanz registrieren | ||
** App mounten | ** App mounten | ||
| − | + | ''' Daten aus Main App senden ''' | |
| + | * Daten definieren und im custom-tag binden | ||
| + | * in der Komponente als props nutzen | ||
| + | ''' Daten aus Komponente an Main App senden ''' | ||
| + | * data() der App definieren | ||
| + | * Funktion mit this.$emit('my-emitter') definieren | ||
| + | * Optional (good practice) in emits: ['my-emitter] listen | ||
main.js | main.js | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
Version vom 2. Januar 2021, 15:21 Uhr
Siehe auch
Vue CLI
Components sind wiederverwendbare Logikbausteine. Du kannst damit z.B. Logik so Kapseln, dass sie nicht durch andere Ereignisse oder Eigenschaften beeinflusst wird.
Durch die Aufteilung einer großen App in kleinere Bausteine wird sie weniger fehleranfällig und leichter wartbar und die Entwicklung gerade bei größeren Apps effektiver.
Eine Component sieht im Template wie ein neuer Tag aus. Damit es keine Namenskonflikte gibt verwendest Du als Name am Besten eine Bezeichnung mit zwei Worten und Bindestrich <contact-details>. Dies kommmt bei normalen Tags nicht vor.
Definiert wird eine Komponente mit der component() Methode. Technisch gesehen ist eine Komponente eine App in einer App. Daher erzeugt man sie auch ganz ähnlich:
const app = Vue.createApp({ //... }) // creation of an app
app.component('single-contact', { //... }) // creation of a component in this app
Im ersten Argument definiert man den Namen über den später als Tag (<single-contact>) zugegriffen werden kann. Im zweiten Argument übergibt man ein Objekt, dass wiederum eigene data, methods... Objekte enthalten kann die aber nur für diese Komponente gelten.
Component Template
Die Component ist also eine Art Mini-App in der Hauptapp. Sie benötigt wie eine App ein Template. Es gibt mehrere Möglichkeiten das Template zu übergeben.
template Object
app.component('friend-contact', {
template: `
<li>
<h2>{{friend.name}}</h2>
<button @click="toggleDetails()">Show Details</button>
<ul v-if="detailsVisible">
<li><strong>Phone:</strong> {{friend.phone}} </li>
<li><strong>Email:</strong> {{friend.email}} </li>
</ul>
</li>
`,// backticks allow multiline code
Quickstart - Component mit CLI
Wenn man mit Components arbeitet bietet sich auch die Arbeit mit dem Vue CLI an. In Kurzform die Schritte die Du benötigst um eine Vue App und Komponenten zu nutzen. Komponente erstellen:
- components/MyComponent.vue
- <template> Abschnitt
- <script>export default{}</script> Abschnitt
Komponente in Main App registrieren
- App.vue erstellen
- <template> und <script> wie oben erstellen
- main.js
- createApp wird beim Erstellen schon eingefügt
- Hauptapp (i.d.R. App.vue) importieren
- Config Oject der Komponenten importieren
- app Instanz über createApp erzeugen
- mit Funktion components in App Instanz registrieren
- App mounten
Daten aus Main App senden
- Daten definieren und im custom-tag binden
- in der Komponente als props nutzen
Daten aus Komponente an Main App senden
- data() der App definieren
- Funktion mit this.$emit('my-emitter') definieren
- Optional (good practice) in emits: ['my-emitter] listen
main.js
import { createApp } from 'vue';
import App from './App.vue';
import MyComponent from './components/MyComponent.vue';
// ...
const app = createApp(App);
app.component('my-component', MyComponent); // always use dashed names thus they can't clash with existing html names
// ...
app.mount('#app');
App.vue
<template>
<h1>The App</h1>
<my-component></my-component>
</template>
<script>
import MyComponent from './components/MyComponent.vue'
export default {
components: {
ActiveUser,
UserData
}
}
</script>
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
Single File Components mit Vue CLI
Wenn man größere Apps bauen und effektiv mit Components arbeiten möchte hilft das Vue CLI und ein Development Setup mit Node.js und npm. Die folgenden Beispiele nutzen Single File Components. Schau unter
Vue CLI
nach wenn du nicht weißt wie man ein solches Setup aufbaut.
Component Communication
Parent - Child Communication mit props
https://v3.vuejs.org/guide/component-props.html
Mit props sendest Du Daten von der Parent App an die Child Component.
App.vue - <template> der Eltern App.
<ul>
<friend-contact
name="Stephan"
phone-number="123"
email-address="my@email.tld">
</friend-contact>
<friend-contact
name="Finn"
phone-number="345"
email-address="my@mymail.tld">
</friend-contact>
</ul>
FriendContact.vue - <script> - Child App
props:[
'name', 'phoneNumber', 'emailAddress'
],
FriendContact.vue - <template>
<h2>{{name}}</h2>
<button @click="toggleDetails"> {{detailsVisible ? 'Hide' : 'Show'}} Details </button>
<ul v-if="detailsVisible">
<li><strong>Phone: </strong>{{phoneNumber}}</li>
<li><strong>Email: </strong>{{emailAddress}}</li>
</ul>
Das props Objekt in der Komponente definiert welche Attribute die Komponente akzeptiert und erwartet. Im der Parent App übergibt man die Daten für diese props einfach als Attribute.
- werden in props der component als camelCase , stehen aber als kebab-case im HTML zur Verfügung (du solltest sie auch so verwenden)
- props stehen mit this.myProp auch in Methoden etc. zur Verfügung
Unidirectional Data Flow
Daten die an die Kind App übertragen werden dürfen nicht mehr verändert werden.
Beispiel: Wenn Du eine prop isFavourite hast und Sie über einen Toggle Button verändern willst kannst du das nicht in der Child App verändern weil die Daten nur von Parent nach Child fließen dürfen. Vue gibt einen Fehler aus.
Es gibt zwei Strategien die man anwenden kann:
- Daten zurück an Parent übertragen (siehe unten)
- Daten nutzen um eine Komponentenvariable zu initialisieren und diese verändern.
Props Object
Anstatt einem Array kann man auch Objekte übertragen. Darin kann man einige Zusatzinformationen unterbringen. Das ist vor allem bei großen Projekten vlt. mit mehreren Programmierern sinnvoll um Komponenten zuverlässiger zu machen. Die zusätzlichen Properties sind festgelegt: type, required
props:['name', 'phoneNumber', 'emailAddress'], // Array
props:{ name: String, phoneNumber: String, emailAddress: String, }, // Object mit Type
props:{
name: { type: String, required: false},
isFavourite: {
type: String,
required: false,
default: '0', // could also be a function: function(){ return: '1'},
validator: function(value){ return value === '0' || value === '1' }
}
}
Mit den zusätzlichen Infos kann vue in der Konsole Fehler ausgeben oder du kannst selber Auswertungen der Daten in der Komponente vornehmen. Erlaubte Types sind:
String, Number, Boolean, Array, Object, Date, Function, Symbol
But type can also be any constructor function (built-in ones like Date or custom ones).
Props mit dynamischen Daten nutzen
Um Props dynamisch zu nutzen kann man sie einfach mit v-bind (oder : ) an Daten binden und mit v-for durchlaufen. Plicht ist jetzt das key Attribut in die man eine eindeutige id geben muss.
App.vue:
//...
friends:[
{
id: 'yvonne',
name: 'Yvonne Oßwald',
phone: '49 123 456',
email: 'post@yvonneosswals.de',
isFavourite: false
},
//...
in <template>
<ul>
<friend-contact
v-for="friend in friends"
:key="friend.id"
:name="friend.name"
:phone-number="friend.phone"
:email-address="friend.email">
</friend-contact>
</ul>
Die Komponente
<li>
<h2>{{name}} {{friendIsFavourite ? '(*)' : ''}}</h2>
<button @click="toggleFavourite">Toggle Favourite</button>
<button @click="toggleDetails"> {{detailsVisible ? 'Hide' : 'Show'}} Details </button>
<ul v-if="detailsVisible">
<li><strong>Phone: </strong>{{phoneNumber}}</li>
<li><strong>Email: </strong>{{emailAddress}}</li>
</ul>
</li>
props:{
name: String,
phoneNumber: String,
emailAddress: String,
isFavourite: {type: Boolean, required: false, default: false}
},
Fallthrough props
Angenommen Du hast eine BaseButton.vue Komponente ohne definierte Props. Im Parent kannst Du trotzdem Attribute und Events nutzen...
<base-button type="submit" @click="doSomething">Click me</base-button>
... und diese dann in der Komponente auswerten. Dafür hat vue ein spezielles $attrs Objekt auf das Du mit
this.$attrs
zugreifen kannst. Dies kann z.b. bei Komponenten mit nur geringer Funktionalität nützlich sein, die eher zur reinen Ausgabe dienen.
Alle props in einem Objekt binden
Angenommen deine Komponente sieht etwas so aus:
UserData.vue
<template>
<h2>{{ firstname }} {{ lastname }}</h2>
</template>
<script>
export default {props: ['firstname', 'lastname'] }
</script>
Anstatt alle Props einzeln zu übertragen kannst du auch ein Übergeordnetes Objekt definieren und dieses übertragen.
App.vue
data() {
return {person: { firstname: 'Max', lastname: 'Muster' } };
}
<!-- Einzelne Attribute binden -->
<user-data :firstname="person.firstname" :lastname="person.lastname"></user-data>
<!-- Alternativ: übergeordnetes Attribut binden (Achtung person ist value des Attributes -->
<user-data v-bind="person"></user-data>
Child - Parent Communication mit custom Events ($emit)
Mit custom Events sendest du Daten von einer Komponente zurück an die Parent app.
Parent App -- [v-bind data] --> Component -- [custom Event data] -->
- Custom Events definierst du mit $emit in der Komponente
- In der Parent App nutzt du das Event im HTML Teil mit v-on
Wenn in einer Komponente Daten verändert werden muss man sie zurück zur Elternkomponente übertragen. Das Ändern der Props ist wegen dem unidirectional dataflow nicht erlaubt.
$emit()
Um Daten zurück zu senden können wir custom Events nutzen. Dazu gibt es die $emit Funktion
this.$emit('event-name', argument1, argument2, ...) // always use kebab-case
- in der Component in einer method this.$emit aufrufen z.b. this.$emit('toggle-state', this.id)
- id ist meist sinnvoll damit in der Parent App der Datensatz zugeordnet werden kann.
- im HTML der Eltern App einen v-on mit diesem Event anlegen z.B. @toggle-state="toggleState"
- in der gebundenen Methode in der Eltern App verarbeiten z.b. toggleState(id){ //ie. store in db }
Beispiel - es soll ein isFavourite Datum aus der Kindkomponente (Friendcontact.vue) gesetzt werden. Mit Klick auf den Button wird ein CustomEvent gesendet. Dieses wird in der Parent App ausgewertet und in den Daten gesezt. Über das v-bind in der Parent App wird es zurück in die h2 der Kind Komponente gesendet und diese aktualisiert.
FriendContact.vue
<template>
<li>
<h2>{{name}} {{isFavourite ? '(*)' : ''}}</h2>
<button @click="toggleFavourite">Toggle Favourite</button>
<!-- ... -->
</li>
</template>
<script>
export default {
props:{
id: {type: String, required: true},
//...
isFavourite: {type: Boolean, required: false, default: false}
},
emits:['toggle-favourite'], // emits object is optional and just for documentation purpose
methods:{
toggleFavourite(){
this.$emit('toggle-favourite', this.id);
}
}
}
</script>
App.vue
<template>
<section>
<header><h1>My Friends</h1></header>
<ul>
<friend-contact
v-for="friend in friends"
:key="friend.id"
:id="friend.id"
:name="friend.name"
//...
:is-favourite="friend.isFavourite"
@toggle-favourite="toggleFavouriteState">
</friend-contact>
</ul>
</section>
</template>
<script>
import FriendContact from './components/FriendContact.vue'
export default {
components: {
FriendContact
},
data(){
return {
friends:[
{
id: 'yvonne',
name: 'Yvonne Oßwald',
phone: '49 123 456',
email: 'post@yvonneosswals.de'
},
{
id: 'finn',
name: 'Finn Schlegel',
phone: '49 456 789',
email: 'post@finnschlegel.de' ,
isFavourite: true
},
]
}
},
methods:{
toggleFavouriteState(friendId){
const identifiedFriend = this.friends.find(
(friend) => friend.id === friendId // eq: function(friend){return friend.id...}
// this callback is executed on every friend (array item)
// first item match is returned
)
// identifiedFriend is a proxy which handles the origin array element of friends array
// thats why we can modify identifiedFriend and friends will be altered too
// console.log('identifiedFriend: ' + identifiedFriend.id)
identifiedFriend.isFavourite = !identifiedFriend.isFavourite
}
}
}
</script>