Улучшение отзывчивости веб-интерфейса часто начинается с оптимизации того, как ваш фронтенд взаимодействует с бэкендом. В нашем основанном на Vue.js Semaphore UI мы внедрили несколько техник, чтобы сделать загрузку данных быстрее и плавнее для пользователей. В этом посте мы делимся тремя наиболее значительными изменениями, которые мы реализовали в версии 2.14.
Почему оптимизировать использование API?
API часто являются основой динамических веб-приложений, но плохо управляемые запросы могут создавать узкие места. Долгое время ожидания последовательных вызовов или избыточная выборка данных ухудшают пользовательский опыт. Давайте решим эти проблемы.
1. Параллельные запросы с Promise.all()
Проблема: Последовательные запросы
Распространенной ошибкой является выполнение API-вызовов один за другим. Например, получение данных пользователя, затем их заказов, затем их предпочтений:
// Последовательный подход (медленно 😞)
async function fetchData() {
const user = await getUser();
const inventory = await getInventory(projectID);
const templates = await getTemplates(projectID);
return { user, inventory, templates };
}
Здесь каждый запрос ждет завершения предыдущего. Если каждый вызов занимает 200 мс, общее время составит 600 мс — заметная задержка.
Решение: Параллельное выполнение
Vue.js использует асинхронные возможности JavaScript, поэтому мы можем отправлять несколько запросов одновременно, используя Promise.all()
:
// Параллельный подход (быстро ⚡)
async function fetchDataParallel() {
const [user, inventory, templates] = await Promise.all([
getUser(),
getInventory(projectID),
getTemplates(projectID),
]);
return { user, inventory, templates };
}
Группируя независимые запросы, все три вызова выполняются одновременно. Если каждый занимает 200 мс, общее время сокращается до 200 мс!
Интеграция с Vue
В компоненте Vue используйте этот шаблон в хуках жизненного цикла или методах:
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("Не удалось загрузить данные:", error);
}
};
}
Совет: Сочетайте это с загрузчиками скелетов, чтобы удерживать пользователей вовлеченными во время загрузки данных.
2. Повторное использование кэшированных ответов API
Проблема: Избыточная выборка
Приложения часто повторно запрашивают одни и те же данные в разных компонентах (например, данные профиля пользователя, запрашиваемые несколькими представлениями). Это тратит пропускную способность и замедляет интерфейс.
Решение: Кэширование и повторное использование
Храните ответы API и повторно используйте их до тех пор, пока они не будут недействительными. Реактивная система Vue делает это простым с помощью централизованного хранилища (например, Pinia или Vuex):
Шаг 1: Создайте кэш-хранилище
// 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];
},
},
});
Шаг 2: Используйте хранилище в компонентах
// Component.vue
import { useApiCache } from '@/stores/apiCache';
export default {
setup() {
const apiCache = useApiCache();
// Повторно использует кэшированного пользователя или запрашивает один раз
const user = apiCache.fetchUser();
return { user };
},
};
Совет: В Semaphore UI мы пока не используем Pinia, но планируем перейти на него в ближайшее время.
Стратегии недействительности кэша
- Истечение по времени: Удаляйте кэшированные данные после установленного времени.
- Ручные триггеры: Недействительность после мутаций (например, когда пользователь обновляет свой профиль).
- Реактивность Vue: Сбрасывайте данные, когда состояние приложения изменяется (например, при выходе из системы).
3. Использование загрузчиков скелетов для более плавных переходов
Проблема: Невозможность взаимодействия с интерфейсом во время загрузки
Даже с оптимизированными вызовами API пользователи все равно сталкиваются с кратковременными задержками во время загрузки данных. Пустые экраны или зависшие интерфейсы в это время могут сделать ваше приложение неотшлифованным или медленным, даже если фактическое время выборки минимально.
Решение: Загрузчики скелетов
Загрузчики-скелеты имитируют макет вашего контента во время загрузки данных, предоставляя визуальную обратную связь, которая удерживает пользователей вовлеченными. В сочетании с параллельными запросами и кэшированием они создают бесшовное восприятие скорости.
Как добавить загрузчики скелетов в Vue.js
Шаг 1: Выберите компонент скелета
В Semaphore UI мы используем фреймворк Vuetify. Он уже включает компонент загрузчика скелета.
<v-skeleton-loader
type="
table-heading,
image,
list-item-two-line,
list-item-two-line,
list-item-two-line"
></v-skeleton-loader>
Шаг 2: Интеграция с асинхронной выборкой данных Используйте состояние загрузки, чтобы переключаться между скелетами и реальным контентом:
// 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;
}
},
};
Шаг 3: Условный рендеринг скелетов
<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>
</v-card>
</div>
</template>
Расширенные советы по дизайну загрузчиков
- Соответствие структуре контента: Разработайте скелеты так, чтобы они отражали ваш фактический макет (например, заполнители изображений, строки текста).
- Приоритизируйте контент выше линии сгиба: Сначала показывайте загрузчики для видимого контента, пока загружаются данные в фоновом режиме.
- Сочетайте с кэшированием: Если кэшированные данные существуют, пропустите скелеты и сразу покажите данные.
Почему это важно
- Пользовательский опыт: 74% пользователей замечают время загрузки (PWA Stats).
- Восприятие производительности: Загрузчики делают ожидания на 15-30% короче (NNGroup Research).
- Доверие к бренду: Отшлифованные переходы сигнализируют о профессионализме.
Подводя итоги
Сочетая параллельные запросы, умное кэширование и загрузчики скелетов, мы смогли добиться значительных улучшений:
- Быстрая начальная загрузка: Параллельные запросы значительно сократили время загрузки данных.
- Плавная навигация: Повторное использование ответов уменьшило избыточные сетевые вызовы.
- Увеличение вовлеченности пользователей: Загрузчики скелетов улучшили визуальную обратную связь и восприятие скорости.
Оптимизация использования API — это не только вопрос производительности, но и улучшение пользовательского опыта. Мы будем продолжать совершенствовать наш фронтенд, чтобы сделать Semaphore UI быстрее и дружелюбнее с каждым обновлением.