← Назад к вопросам
Какую из задач с одинаковым приоритетам выберешь первой?
2.0 Middle🔥 131 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как настроить взаимодействие с Backend через REST API во Vue приложении?
Взаимодействие с REST API — один из ключевых аспектов современного веб-разработки. В Vue приложениях существует несколько подходов для работы с backend через HTTP.
1. Встроенный Fetch API
Самый базовый и встроенный способ работы с HTTP запросами.
// composables/useApi.ts
import { ref } from 'vue';
export function useApi() {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
const fetchData = async (url, options = {}) => {
loading.value = true;
error.value = null;
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`,
...options.headers,
},
...options,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (err) {
error.value = err.message;
console.error('API error:', err);
} finally {
loading.value = false;
}
return data.value;
};
return { data, error, loading, fetchData };
}
// Использование в компоненте
<template>
<div>
<button @click="loadUsers">Load Users</button>
<div v-if="loading">Loading...</div>
<div v-if="error">{{ error }}</div>
<ul v-if="data">
<li v-for="user in data" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script setup>
import { useApi } from './composables/useApi';
const { data, error, loading, fetchData } = useApi();
const loadUsers = () => {
fetchData('https://api.example.com/users');
};
</script>
2. Axios — популярная HTTP библиотека
Axios предоставляет удобный интерфейс и перехватчики (interceptors) для глобальной обработки ошибок.
// npm install axios
// utils/api.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// Перехватчик для добавления токена
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Перехватчик для обработки ошибок
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Токен истек, редирект на логин
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default apiClient;
// composables/useUsers.ts
import { ref } from 'vue';
import apiClient from '../utils/api';
export function useUsers() {
const users = ref([]);
const loading = ref(false);
const error = ref(null);
const fetchUsers = async () => {
loading.value = true;
try {
const response = await apiClient.get('/users');
users.value = response.data;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
const createUser = async (userData) => {
try {
const response = await apiClient.post('/users', userData);
users.value.push(response.data);
return response.data;
} catch (err) {
error.value = err.message;
throw err;
}
};
const updateUser = async (id, userData) => {
try {
const response = await apiClient.patch(`/users/${id}`, userData);
const index = users.value.findIndex((u) => u.id === id);
if (index !== -1) {
users.value[index] = response.data;
}
return response.data;
} catch (err) {
error.value = err.message;
throw err;
}
};
const deleteUser = async (id) => {
try {
await apiClient.delete(`/users/${id}`);
users.value = users.value.filter((u) => u.id !== id);
} catch (err) {
error.value = err.message;
throw err;
}
};
return { users, loading, error, fetchUsers, createUser, updateUser, deleteUser };
}
3. Кэширование результатов запросов
// composables/useCachedApi.ts
import { ref } from 'vue';
const cache = new Map();
export function useCachedApi() {
const fetchData = async (url, options = {}) => {
// Проверить кэш
if (cache.has(url) && !options.skipCache) {
return cache.get(url);
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
...options.headers,
},
...options,
});
const data = await response.json();
// Сохранить в кэш
cache.set(url, data);
// Очистить кэш через определенное время
if (options.cacheTime) {
setTimeout(() => cache.delete(url), options.cacheTime);
}
return data;
};
const clearCache = () => cache.clear();
return { fetchData, clearCache };
}
4. Обработка ошибок и retry логика
// composables/useRetryApi.ts
export function useRetryApi() {
const fetchWithRetry = async (url, options = {}, maxRetries = 3) => {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
...options.headers,
},
...options,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
lastError = error;
// Не повторяй при 4xx ошибках (кроме 429)
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
throw error;
}
// Экспоненциальная задержка перед повтором
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw lastError;
};
return { fetchWithRetry };
}
5. Обработка загрузки файлов
// composables/useFileUpload.ts
export function useFileUpload() {
const uploadFile = async (file, url) => {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
body: formData,
});
if (!response.ok) {
throw new Error('Upload failed');
}
return await response.json();
} catch (error) {
console.error('Upload error:', error);
throw error;
}
};
return { uploadFile };
}
6. Real-time обновления с WebSocket
// composables/useWebSocket.ts
import { ref } from 'vue';
export function useWebSocket(url) {
const data = ref(null);
const connected = ref(false);
const ws = ref(null);
const connect = () => {
ws.value = new WebSocket(url);
ws.value.onopen = () => {
connected.value = true;
};
ws.value.onmessage = (event) => {
data.value = JSON.parse(event.data);
};
ws.value.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.value.onclose = () => {
connected.value = false;
};
};
const send = (message) => {
if (ws.value?.readyState === WebSocket.OPEN) {
ws.value.send(JSON.stringify(message));
}
};
const disconnect = () => {
ws.value?.close();
};
return { data, connected, connect, send, disconnect };
}
7. Полный пример компонента с API
<template>
<div class="users-container">
<button @click="loadUsers" :disabled="loading">
{{ loading ? 'Loading...' : 'Load Users' }}
</button>
<div v-if="error" class="error">{{ error }}</div>
<ul v-if="users.length > 0">
<li v-for="user in users" :key="user.id" class="user-item">
{{ user.name }} ({{ user.email }})
<button @click="deleteUser(user.id)">Delete</button>
</li>
</ul>
</div>
</template>
<script setup>
import { useUsers } from './composables/useUsers';
import { onMounted } from 'vue';
const { users, loading, error, fetchUsers, deleteUser } = useUsers();
onMounted(() => {
fetchUsers();
});
</script>
<style scoped>
.error {
color: red;
padding: 10px;
background: #ffe0e0;
border-radius: 4px;
}
.user-item {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #eee;
}
</style>
Best Practices
- Используй composables для логики API
- Абстрагируй HTTP клиент (Axios или Fetch wrapper)
- Обрабатывай ошибки глобально через перехватчики
- Кэшируй данные где необходимо
- Используй proper HTTP методы: GET, POST, PATCH, DELETE
- Валидируй данные перед отправкой и после получения
- Добавь timeout для запросов
- Логируй ошибки для отладки
Соблюдение этих практик обеспечит надежное и масштабируемое взаимодействие с backend в Vue приложениях.