Listen-Rendering und Vue’s v-for-Direktive

Avatar of Hassan Djirdeh
Hassan Djirdeh am

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

Listen-Rendering ist eine der am häufigsten verwendeten Praktiken in der Frontend-Webentwicklung. Dynamisches Listen-Rendering wird oft verwendet, um eine Reihe von ähnlich gruppierten Informationen in einem prägnanten und benutzerfreundlichen Format für den Benutzer darzustellen. In fast jeder Webanwendung, die wir nutzen, sehen wir *Listen* von Inhalten in zahlreichen Bereichen der App.

In diesem Artikel werden wir ein Verständnis für Vue's v-for Direktive zum Generieren dynamischer Listen gewinnen und einige Beispiele durchgehen, warum das key Attribut dabei verwendet werden sollte.

Da wir die Dinge gründlich erklären werden, während wir mit dem Codieren beginnen, geht dieser Artikel davon aus, dass Sie keine oder nur sehr geringe Kenntnisse von Vue (und/oder anderen JavaScript-Frameworks) haben.

Fallstudie: Twitter

Wir werden Twitter als Fallstudie für diesen Artikel verwenden.

Wenn wir eingeloggt sind und uns in der Haupt-Indexroute von Twitter befinden, wird uns eine Ansicht präsentiert, die dieser ähnelt

A screenshot of the Twitter homepage displaying a list of tweets from the author's timeline.

Auf der Homepage sind wir es gewohnt, eine Liste von Trends, eine Liste von Tweets, eine Liste potenzieller Follower usw. zu sehen. Der angezeigte Inhalt in diesen Listen hängt von einer Vielzahl von Faktoren ab – unserer Twitter-Historie, wem wir folgen, unseren Likes usw. Als Ergebnis können wir sagen, dass all diese Daten *dynamisch* sind.

Obwohl diese Daten dynamisch abgerufen werden, bleibt die *Art und Weise*, wie diese Daten angezeigt werden, gleich. Dies liegt teilweise an der Verwendung von **wiederverwendbaren Webkomponenten**.

Zum Beispiel; wir können die Liste der Tweets als eine Liste einzelner tweet-component Elemente sehen. Wir können tweet-component als eine Hülle betrachten, die verschiedene Daten wie Benutzername, Handle, Tweet und Avatar unter anderem aufnimmt und diese Stücke einfach in einem konsistenten Markup anzeigt.

A diagram that dissects the elements of a tweet, including the username, handle, avatar, tweet body and like count.

Nehmen wir an, wir wollten eine Liste von Komponenten (z. B. eine Liste von tweet-component Elementen) basierend auf einer großen Datenquelle von einem Server rendern. In Vue sollte als Erstes die **v-for** Direktive in den Sinn kommen, um dies zu erreichen.

Die v-for Direktive

Die v-for Direktive wird verwendet, um eine Liste von Elementen basierend auf einer Datenquelle zu rendern. Die Direktive kann auf einem Template-Element verwendet werden und erfordert eine spezifische Syntax in der Art von

A visual showing a Vue for directive of v-for equals item in items, where item is the alias and items is the data collection.

Schauen wir uns ein Beispiel in der Praxis an. Zuerst gehen wir davon aus, dass wir bereits eine Sammlung von Tweet-Daten erhalten haben

const tweets = [
  {
    id: 1,
    name: 'James',
    handle: '@jokerjames',
    img: 'https://semantic-ui.com/images/avatar2/large/matthew.png',
    tweet: "If you don't succeed, dust yourself off and try again.",
    likes: 10,
  },
  { 
    id: 2,
    name: 'Fatima',
    handle: '@fantasticfatima',
    img: 'https://semantic-ui.com/images/avatar2/large/molly.png',
    tweet: 'Better late than never but never late is better.',
    likes: 12,
  },
  {
    id: 3,
    name: 'Xin',
    handle: '@xeroxin',
    img: 'https://semantic-ui.com/images/avatar2/large/elyse.png',
    tweet: 'Beauty in the struggle, ugliness in the success.',
    likes: 18,
  }
]

tweets ist eine Sammlung von tweet Objekten, wobei jedes tweet Details dieses speziellen Tweets enthält – eine eindeutige Kennung, den Namen/Handle des Kontos, die Tweet-Nachricht usw. Versuchen wir nun, die v-for Direktive zu verwenden, um eine Liste von Tweet-Komponenten basierend auf diesen Daten zu rendern.

Zuallererst erstellen wir die Vue-Instanz – das Herzstück der Vue-Anwendung. Wir werden unsere Instanz an ein DOM-Element mit der ID app anhängen/mounten und die tweets Sammlung als Teil des Datenobjekts der Instanz zuweisen.

new Vue({
  el: '#app',
  data: {
    tweets
  }
});

Nun werden wir eine tweet-component erstellen, die unsere v-for Direktive zum Rendern einer Liste verwendet. Wir werden den globalen Vue.component Konstruktor verwenden, um eine Komponente namens tweet-component zu erstellen

Vue.component('tweet-component', {
  template: `  
    <div class="tweet">
      <div class="box">
        <article class="media">
          <div class="media-left">
            <figure class="image is-64x64">
              <img :src="tweet.img" alt="Image">
            </figure>
          </div>
          <div class="media-content">
            <div class="content">
              <p>
                <strong>{{tweet.name}}</strong> <small>{{tweet.handle}}</small>
                <br>
                {{tweet.tweet}}
              </p>
            </div>
              <div class="level-left">
                <a class="level-item">
                  <span class="icon is-small"><i class="fas fa-heart"></i></span>
                  <span class="likes">{{tweet.likes}}</span>
                </a>
              </div>
          </div>
        </article>
      </div>
    </div>  
  `,
  props: {
    tweet: Object
  }
});

Ein paar interessante Dinge, die hier zu beachten sind.

  1. Die tweet-component erwartet ein tweet Objekt-Prop, wie in der Prop-Validierungsanforderung (props: {tweet: Object}) zu sehen ist. Wenn die Komponente mit einem tweet Prop gerendert wird, das *kein Objekt* ist, gibt Vue Warnungen aus.
  2. Wir binden die Eigenschaften des Tweet-Objekt-Props mit Hilfe der Mustache-Syntax: {{ }} an das Komponententemplate.
  3. Das Komponenten-Markup adaptiert das Bulma Box-Element, da es eine gute Ähnlichkeit mit einem Tweet darstellt.

Im HTML-Template müssen wir das Markup erstellen, wo unsere Vue-App gemountet wird (d. h. das Element mit der ID app). Innerhalb dieses Markups verwenden wir die v-for Direktive, um eine Liste von Tweets zu rendern. Da tweets die Datensammlung ist, über die wir iterieren werden, ist tweet ein geeigneter Alias, der in der Direktive verwendet werden kann. In jeder gerenderten tweet-component übergeben wir *auch* das iterierte tweet Objekt als Props, damit es in der Komponente zugänglich ist.

<div id="app" class="columns">
  <div class="column">
    <tweet-component v-for="tweet in tweets" :tweet="tweet"/>
  </div>
</div>

Unabhängig davon, *wie viele* weitere Tweet-Objekte der Sammlung hinzugefügt würden; oder wie sie sich im Laufe der Zeit ändern – unser Setup wird immer alle Tweets in der Sammlung im gleichen erwarteten Markup rendern.

Mit Hilfe von etwas benutzerdefiniertem CSS wird unsere App ungefähr so aussehen

Siehe den Pen Simple Twitter Feed #1 von Hassan Dj (@itslit) auf CodePen.

Obwohl alles wie erwartet funktioniert, wird uns möglicherweise ein Vue-Tipp in unserer Browserkonsole angezeigt

[Vue tip]: <tweet-component v-for="tweet in tweets">: component lists rendered with v-for should have explicit keys...

Möglicherweise sehen Sie die Warnung in der Browserkonsole nicht, wenn Sie den Code über CodePen ausführen.

Warum sagt uns Vue, dass wir explizite Schlüssel in unserer Liste angeben sollen, wenn alles wie erwartet funktioniert?

Schlüssel

Es ist gängige Praxis, jedem iterierten Element innerhalb einer gerenderten v-for Liste ein Schlüsselattribut zuzuweisen. Das liegt daran, dass Vue das key Attribut verwendet, um **eindeutige Bindungen für die Identität jedes Knotens** zu erstellen.

Lassen Sie uns das weiter erklären – wenn es dynamische UI-Änderungen in unserer Liste gäbe (z. B. die Reihenfolge der Listenelemente wird neu angeordnet), würde Vue dazu neigen, Daten innerhalb jedes Elements zu ändern, *anstatt* die DOM-Elemente entsprechend zu verschieben. Dies wird in den meisten Fällen kein Problem sein. In bestimmten Fällen, in denen unsere v-for Liste vom DOM-Status und/oder dem Status von Kind-Komponenten abhängt, kann dies jedoch zu einigen unerwarteten Verhaltensweisen führen.

Schauen wir uns ein Beispiel dafür an. Was wäre, wenn unsere einfache Tweet-Komponente nun ein Eingabefeld enthalten würde, das es dem Benutzer erlaubt, direkt auf die Tweet-Nachricht zu antworten? Wir ignorieren, wie diese Antwort übermittelt werden könnte, und konzentrieren uns einfach auf das neue Eingabefeld selbst

A mockup of the tweet we are trying to create that looks exactly like the one diagramed earlier. This one has an additional component that includes a text input for submitting a reply.

Wir fügen dieses neue Eingabefeld in das Template von tweet-component ein

Vue.component('tweet-component', {
  template: `
    <div class="tweet">
      <div class="box">
        // ...
      </div>
      <div class="control has-icons-left has-icons-right">
        <input class="input is-small" placeholder="Tweet your reply..." />
        <span class="icon is-small is-left">
          <i class="fas fa-envelope"></i>
        </span>
      </div>
    </div>
  `,
  props: {
    tweet: Object
  }
});

Nehmen wir an, wir möchten eine weitere neue Funktion in unsere App einführen. Diese Funktion würde es dem Benutzer ermöglichen, eine Liste von Tweets zufällig zu mischen.

Um dies zu tun; können wir zuerst einen "Shuffle!"-Button in unser HTML-Template einfügen

<div id="app" class="columns">
  <div class="column">
    <button class="is-primary button" @click="shuffle">Shuffle!</button>
    <tweet-component  v-for="tweet in tweets" :tweet="tweet"/>
  </div>
</div>

Wir haben einen Click-Event-Listener an das Button-Element angehängt, der eine shuffle Methode aufruft, wenn er ausgelöst wird. In unserer Vue-Instanz; erstellen wir die shuffle Methode, die für das zufällige Mischen der tweets Sammlung in der Instanz verantwortlich ist. Wir werden lodash's _shuffle Methode verwenden, um dies zu erreichen

new Vue({
  el: '#app',
  data: {
    tweets
  },
  methods: {
    shuffle() {
      this.tweets = _.shuffle(this.tweets)
    }
  }
});

Lassen Sie es uns ausprobieren! Wenn wir ein paar Mal auf Shuffle klicken; werden wir feststellen, dass sich unsere Tweet-Elemente mit jedem Klick zufällig anordnen.

Siehe den Pen Simple Twitter Feed #2 von Hassan Dj (@itslit) auf CodePen.

Wenn wir jedoch einige Informationen in das Eingabefeld jeder Komponente eingeben *und dann* auf Shuffle klicken; werden wir feststellen, dass etwas Seltsames passiert

An animated screenshot of a Shuffle button being clicked. The order of the tweets is shuffled, but the text fields for submitting replies are not.

Da wir uns nicht für die Verwendung des **key** Attributs entschieden haben, hat Vue keine eindeutigen Bindungen für jeden Tweet-Knoten erstellt. Infolgedessen, wenn wir versuchen, die Tweets neu anzuordnen, wählt Vue den performanteren Speicheransatz, um einfach Daten in jedem Element zu *ändern (oder patchen)*. Da der temporäre DOM-Status (d. h. der eingegebene Text) bestehen bleibt, erleben wir diese unbeabsichtigte Diskrepanz.

Hier ist ein Diagramm, das uns die Daten zeigt, die auf jedes Element gepatcht werden, und den DOM-Status, der bestehen bleibt

A diagram of a tweet outlining the patched items that are included in the shuffle and the text field that is a DOM element and remains in place.

Um dies zu vermeiden; müssen wir jedem in der Liste gerenderten tweet-component einen eindeutigen **Schlüssel** zuweisen. Wir werden die id eines tweet als eindeutige Kennung verwenden, da wir sicher davon ausgehen können, dass die id eines Tweets nicht gleich der eines anderen sein sollte. Da wir dynamische Werte verwenden, werden wir die v-bind Direktive verwenden, um unseren Schlüssel an tweet.id zu binden

<div id="app" class="columns">
  <div class="column">
    <button class="is-primary button" @click="shuffle">Shuffle!</button>
    <tweet-component  v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" />
  </div>
</div>

Jetzt erkennt Vue die Identität jedes Tweet-Knotens; und wird daher die Komponenten *neu anordnen*, wenn wir beabsichtigen, die Liste zu mischen.

An animated screenshot of the tweets shuffling correctly when the Shuffle button is clicked. Now, all elements of the tweet are included in the shuffle, including the text field.

Da jede Tweet-Komponente nun entsprechend verschoben wird, können wir dies noch weiter treiben und Vue's transition-group verwenden, um zu *zeigen*, wie die Elemente neu angeordnet werden.

Um dies zu tun, fügen wir das transition-group Element als Wrapper für die v-for Liste hinzu. Wir werden einen Transitionsnamen von tweets angeben und deklarieren, dass die Transition-Gruppe als div Element gerendert werden soll.

<div id="app" class="columns">
  <div class="column">
    <button class="is-primary button" @click="shuffle">Shuffle!</button>
    <transition-group name="tweets" tag="div">
      <tweet-component  v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" />
    </transition-group>
  </div>
</div>

Basierend auf dem Namen der Transition erkennt Vue automatisch, ob CSS-Übergänge/Animationen angegeben wurden. Da wir einen Übergang für die *Bewegung* von Elementen in der Liste hervorrufen möchten; sucht Vue nach einem angegebenen CSS-Übergang in der Art von tweets-move (wobei tweets der Name ist, der unserer Transition-Gruppe gegeben wurde). Folglich werden wir manuell eine .tweets-move Klasse einführen, die einen angegebenen Typ und eine Zeit für den Übergang hat

#app .tweets-move {
  transition: transform 1s;
}

Dies ist ein sehr kurzer Einblick in die Anwendung von Listenübergängen. Schauen Sie sich unbedingt die Vue-Dokumentation an, um detaillierte Informationen zu allen verschiedenen Arten von Übergängen zu erhalten, die angewendet werden können!

Unsere tweet-component Elemente werden nun beim Auslösen eines Shuffle ordnungsgemäß zwischen den Positionen *übergehen*. Probieren Sie es aus! Geben Sie etwas in die Eingabefelder ein und klicken Sie ein paar Mal auf "Shuffle!".

Siehe den Pen Simple Twitter Feed #3 von Hassan Dj (@itslit) auf CodePen.

Ziemlich cool, oder? Ohne das key Attribut *kann das transition-group Element nicht zur Erstellung von Listenübergängen verwendet werden*, da die Elemente *an Ort und Stelle gepatcht* werden, anstatt neu angeordnet zu werden.

Sollte das key Attribut immer verwendet werden? **Es wird empfohlen**. Die Vue-Dokumentation besagt, dass das Schlüsselattribut nur dann weggelassen werden sollte, wenn

  • Wir wollen absichtlich das Standardverhalten des Patchens von Elementen an Ort und Stelle aus Leistungsgründen.
  • Der DOM-Inhalt ist einfach genug.

Fazit

Und da haben wir es! Hoffentlich hat dieser kurze Artikel gezeigt, wie nützlich die v-for Direktive ist, und etwas mehr Kontext dazu gegeben, warum das key Attribut oft verwendet wird. Lassen Sie mich wissen, wenn Sie Fragen/Gedanken haben!


Wenn Ihnen mein Schreibstil gefallen hat und Sie potenziell daran interessiert sind, wie man Apps mit Vue.js erstellt, gefällt Ihnen vielleicht das Buch **Fullstack Vue: The Complete Guide to Vue.js**, das ich mit veröffentlicht habe! Das Buch behandelt zahlreiche Facetten von Vue, darunter, aber nicht beschränkt auf Routing, einfaches State Management, Formularbehandlung, Vuex, Serverpersistenz und Testing. Wenn Sie interessiert sind, erhalten Sie weitere Informationen auf unserer Website, **https://fullstack.io/vue**.