Forbedring af webgrænsefladens responstid starter ofte med at optimere, hvordan dit frontend kommunikerer med backend. I vores Vue.js-baserede Semaphore UI introducerede vi flere teknikker til at gøre dataindlæsning hurtigere og mere smidig for brugerne. Dette indlæg deler de tre mest virkningsfulde ændringer, vi implementerede i version 2.14.
Hvorfor optimere API-brug?
API’er er ofte rygraden i dynamiske webapps, men dårligt administrerede anmodninger kan skabe flaskehalse. Lange ventetider på sekventielle kald eller overflødig datahentning forringer brugeroplevelsen. Lad os tage hånd om disse problemer direkte.
1. Parallelle anmodninger med Promise.all()
Problemet: Sekventielle anmodninger
En almindelig faldgrube er at foretage API-kald efter hinanden. For eksempel at hente brugeroplysninger, derefter deres ordrer, derefter deres præferencer:
// Sekventiel tilgang (langsom 😞)
async function fetchData() {
const user = await getUser();
const inventory = await getInventory(projectID);
const templates = await getTemplates(projectID);
return { user, inventory, templates };
}
Her venter hver anmodning på, at den forrige afsluttes. Hvis hvert kald tager 200 ms, er den samlede tid 600 ms — en mærkbar forsinkelse.
Løsningen: Parallel udførelse
Vue.js udnytter JavaScripts asynkrone funktioner, så vi kan affyre flere anmodninger samtidigt ved hjælp af Promise.all():
// Parallel tilgang (hurtig ⚡)
async function fetchDataParallel() {
const [user, inventory, templates] = await Promise.all([
getUser(),
getInventory(projectID),
getTemplates(projectID),
]);
return { user, inventory, templates };
}
Ved at gruppere uafhængige anmodninger behandles alle tre kald samtidigt. Hvis hver tager 200 ms, falder den samlede tid til 200 ms!
Integration med Vue
Brug dette mønster i lifecycle-hooks eller metoder i en Vue-komponent:
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("Failed loading data:", error);
}
};
}
}
Tip: Kombiner dette med skeleton loaders for at holde brugerne engagerede, mens data indlæses.
2. Genbrug af cachede API-svar
Problemet: Redundant datahentning
Apps henter ofte de samme data igen på tværs af komponenter (f.eks. brugerprofildata, som efterspørges af flere visninger). Dette spilder båndbredde og gør grænsefladen langsommere.
Løsningen: Cache og genbrug
Gem API-svar og genbrug dem, indtil de invalideres. Vues reaktivitetssystem gør dette nemt med et centraliseret store (såsom Pinia eller Vuex):
Trin 1: Opret et 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];
},
},
});
Trin 2: Brug store i komponenter
// Component.vue
import { useApiCache } from '@/stores/apiCache';
export default {
setup() {
const apiCache = useApiCache();
// Genbruger cachet bruger eller henter én gang
const user = apiCache.fetchUser();
return { user };
},
};
Tip: I Semaphore UI bruger vi endnu ikke Pinia, men vi planlægger snart at migrere til det.
Strategier til cache-invalidering
- Tidsbaseret udløb: Slet cachede data efter en fastsat varighed.
- Manuelle triggers: Invalider efter mutationer (f.eks. når en bruger opdaterer sin profil).
- Vues reaktivitet: Nulstil data, når appens tilstand ændres (f.eks. ved logout).
3. Brug af skeleton loaders til mere smidige overgange
Problemet: Uresponsiv UI under indlæsning
Selv med optimerede API-kald oplever brugere stadig korte forsinkelser, mens data indlæses. Tomme skærme eller frosne grænseflader i dette tidsrum kan få din app til at føles ufærdig eller langsom, selvom den faktiske hentetid er minimal.
Løsningen: Skeleton loaders
Skeleton-pladsholdere efterligner dit indholds layout, mens data indlæses, og giver visuel feedback, der fastholder brugerens engagement. Kombineret med parallelle anmodninger og caching skaber de en sømløs opfattelse af hastighed.
Sådan tilføjes skeleton loaders i Vue.js
Trin 1: Vælg skeleton-komponent
I Semaphore UI bruger vi Vuetify-frameworket. Det inkluderer allerede en skeleton loader-komponent.
<v-skeleton-loader
type="
table-heading,
image,
list-item-two-line,
list-item-two-line,
list-item-two-line"
></v-skeleton-loader>
Trin 2: Integrer med asynkron datahentning Brug en loading-tilstand til at skifte mellem skeletons og det reelle indhold:
// 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;
}
},
};
Trin 3: Vis skeletons betinget
<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>
Avancerede tips til loader-design
- Match indholdsstruktur: Design skeletons, så de spejler dit faktiske layout (f.eks. billedpladsholdere, tekstlinjer).
- Prioriter synligt indhold (above-the-fold): Vis først loaders for synligt indhold, mens baggrundsdata afsluttes.
- Kombiner med caching: Hvis cachede data findes, springes skeletons over, og data vises med det samme.
Hvorfor dette betyder noget
- Brugeroplevelse: 74% af brugerne bemærker indlæsningstider (PWA Stats).
- Oplevet ydeevne: Loaders får ventetid til at føles 15-30% kortere (NNGroup Research).
- Brand-tillid: Gennemførte overgange signalerer professionalisme.
Sammenfatning
Ved at kombinere parallelle anmodninger, intelligent caching og skeleton loaders var vi i stand til at opnå betydelige forbedringer:
- Hurtigere indledende indlæsning: Parallelle anmodninger reducerede dataindlæsningstiden markant.
- Mere glidende navigation: Genbrugte svar mindskede overflødige netværkskald.
- Forbedret brugerengagement: Skeleton loaders forbedrede den visuelle feedback og den oplevede hastighed.
Optimering af API-brug handler ikke kun om rå ydeevne — det forbedrer også brugeroplevelsen. Vi vil fortsætte med at forfine vores frontend for at gøre Semaphore UI hurtigere og mere brugervenlig med hver opdatering.

