Improving web interface responsiveness often starts with optimizing how your frontend communicates with the backend. In our Vue.js-basierten Semaphore UI haben wir mehrere Techniken eingeführt, um das Laden von Daten für die Benutzer schneller und reibungsloser zu gestalten. Dieser Beitrag teilt die drei wirkungsvollsten Änderungen, die wir in Version 2.14 implementiert haben.


Warum API-Nutzung optimieren?

APIs sind oft das Rückgrat dynamischer Webanwendungen, aber schlecht verwaltete Anfragen können Engpässe verursachen. Lange Wartezeiten bei sequenziellen Aufrufen oder redundantes Abrufen von Daten verschlechtern die Benutzererfahrung. Lassen Sie uns diese Probleme direkt angehen.


1. Parallele Anfragen mit Promise.all()

Das Problem: Sequenzielle Anfragen

Eine häufige Falle ist es, API-Aufrufe nacheinander zu machen. Zum Beispiel, zuerst Benutzerdaten abzurufen, dann deren Bestellungen, dann deren Präferenzen:

// Sequenzieller Ansatz (langsam 😞)
async function fetchData() {
  const user = await getUser();
  const inventory = await getInventory(projectID);
  const templates = await getTemplates(projectID);
  return { user, inventory, templates };
}

Hier wartet jede Anfrage, bis die vorherige abgeschlossen ist. Wenn jeder Aufruf 200 ms dauert, beträgt die Gesamtdauer 600 ms—eine spürbare Verzögerung.

Die Lösung: Parallele Ausführung

Vue.js nutzt die asynchronen Fähigkeiten von JavaScript, sodass wir mehrere Anfragen gleichzeitig mit Promise.all() auslösen können:

// Paralleler Ansatz (schnell ⚡)
async function fetchDataParallel() {
  const [user, inventory, templates] = await Promise.all([
    getUser(),
    getInventory(projectID),
    getTemplates(projectID),
  ]);
  return { user, inventory, templates };
}

Durch das Gruppieren unabhängiger Anfragen werden alle drei Aufrufe gleichzeitig aufgelöst. Wenn jeder 200 ms dauert, sinkt die Gesamtdauer auf 200 ms!

Integration mit Vue

In einer Vue-Komponente verwenden Sie dieses Muster in Lebenszyklus-Hooks oder Methoden:

export default {
  props: {
    projectID: Number,
  },
  watch: {
    async projectID() {
      await this.fetchDataParallel();
    },
  },
  async created() {
    await this.fetchDataParallel();
  },
  methods: {
    async fetchDataParallel() {
      try {
        const [
          this.user, 
          this.inventory,
          this.templates
        ] = await Promise.all([
          getUser(),
          getInventory(this.projectID),
          getTemplates(this.projectID),
        ]);
      } catch (error) {
        console.error("Fehler beim Laden der Daten:", error);
      }
    };
  }

Tipp: Kombinieren Sie dies mit Skeleton-Loadern, um die Benutzer während des Ladevorgangs zu beschäftigen.


2. Wiederverwendung von zwischengespeicherten API-Antworten

Das Problem: Redundantes Abrufen

Apps rufen oft dieselben Daten in verschiedenen Komponenten erneut ab (z. B. Benutzerdaten, die von mehreren Ansichten angefordert werden). Dies verschwendet Bandbreite und verlangsamt die Benutzeroberfläche.

Die Lösung: Zwischenspeichern und Wiederverwenden

Speichern Sie API-Antworten und verwenden Sie sie, bis sie ungültig werden. Das Reaktivitätssystem von Vue macht dies einfach mit einem zentralen Store (wie Pinia oder Vuex):

Schritt 1: Erstellen Sie einen Cache-Store

// stores/apiCache.js
import { defineStore } from 'pinia';

export const useApiCache = defineStore('apiCache', {
  state: () => ({
    project: null,
    templates: {},
  }),
  actions: {
    async fetchProject() {
      if (!this.project) {
        this.project = await getProject();
      }
      return this.project;
    },
    async fetchTemplates(id) {
      if (!this.templates[id]) {
        this.templates[id] = await fetchTemplates(id);
      }
      return this.templates[id];
    },
  },
});

Schritt 2: Verwenden Sie den Store in Komponenten

// Component.vue
import { useApiCache } from '@/stores/apiCache';

export default {
  setup() {
    const apiCache = useApiCache();

    // Wiederverwendet zwischengespeicherten Benutzer oder ruft einmal ab
    const user = apiCache.fetchUser();

    return { user };
  },
};

Tipp: In Semaphore UI verwenden wir noch kein Pinia, planen jedoch bald die Migration.

Strategien zur Ungültigmachung des Caches

3. Verwendung von Skeleton-Loadern für reibungslosere Übergänge

Das Problem: Unresponsive UI während des Ladens

Selbst mit optimierten API-Aufrufen stehen Benutzer während des Ladens von Daten vor kurzen Verzögerungen. Leere Bildschirme oder eingefrorene Oberflächen während dieser Zeit können Ihre App unpoliert oder langsam erscheinen lassen, selbst wenn die tatsächliche Abrufzeit minimal ist.

Die Lösung: Skeleton-Loader

Skeleton-Platzhalter ahmen das Layout Ihrer Inhalte nach, während Daten geladen werden, und bieten visuelles Feedback, das die Benutzer beschäftigt hält. In Kombination mit parallelen Anfragen und Caching schaffen sie eine nahtlose Wahrnehmung von Geschwindigkeit.


So fügen Sie Skeleton-Loader in Vue.js hinzu

Schritt 1: Wählen Sie die Skeleton-Komponente

In Semaphore UI verwenden wir das Vuetify-Framework. Es enthält bereits eine Skeleton-Loader-Komponente.

<v-skeleton-loader
  type="
    table-heading,
    image,
    list-item-two-line,
    list-item-two-line,
    list-item-two-line"
></v-skeleton-loader>

Schritt 2: Integrieren Sie mit asynchronem Datenabruf Verwenden Sie einen Ladezustand, um zwischen Skeletons und echtem Inhalt umzuschalten:

// Component.vue
export default {
  data() {
    return {
      isLoading: true,
      user: null,
      templates: [],
    };
  },
  async created() {
    this.isLoading = true;
    try {
      [this.user, this.templates] = await Promise.all([
        fetchUser(),
        fetchTemplates(),
      ]);
    } finally {
      this.isLoading = false;
    }
  },
};

Schritt 3: Bedingtes Rendern von Skeletons

<template>
  <div class="user-profile">
    <v-skeleton-loader
      v-if="isLoading"
      type="
        table-heading,
        image,
        list-item-two-line,
        list-item-two-line,
        list-item-two-line"
    ></v-skeleton-loader>
    <v-card v-else>
      <h2>{{ user.name }}</h2>

      <v-card v-for="tpl in templates" :key="tpl.id">
        <v-card-title>{{ tpl.name }}</v-card-title>
        <v-card-text>{{ tpl.description }}</v-card-text>
      </v-card>
    </div>
  </div>
</template>

Fortgeschrittene Tipps für das Design von Loadern

  1. Inhaltstruktur anpassen: Gestalten Sie Skeletons so, dass sie Ihr tatsächliches Layout widerspiegeln (z. B. Platzhalter für Bilder, Textzeilen).
  2. Priorisieren Sie Inhalte über der Falte: Zeigen Sie Loader für sichtbare Inhalte zuerst an, während Hintergrunddaten geladen werden.
  3. Kombinieren Sie mit Caching: Wenn zwischengespeicherte Daten vorhanden sind, überspringen Sie Skeletons und zeigen Sie die Daten sofort an.

Warum das wichtig ist


Alles zusammenfassen

Durch die Kombination von parallelen Anfragen, intelligentem Caching und Skeleton-Loadern konnten wir erhebliche Verbesserungen erzielen:

  1. Schnellere anfängliche Ladezeiten: Parallele Anfragen verkürzen die Datenladezeiten erheblich.
  2. Reibungslosere Navigation: Wiederverwendete Antworten reduzierten redundante Netzwerkaufrufe.
  3. Verbesserte Benutzerbindung: Skeleton-Loader verbesserten visuelles Feedback und wahrgenommene Geschwindigkeit.

Die Optimierung der API-Nutzung geht nicht nur um rohe Leistung—sie verbessert auch die Benutzererfahrung. Wir werden weiterhin unser Frontend verfeinern, um Semaphore UI mit jedem Update schneller und benutzerfreundlicher zu gestalten.