\n```\n\n## 2. Axios — популярная HTTP библиотека\n\nAxios предоставляет удобный интерфейс и перехватчики (interceptors) для глобальной обработки ошибок.\n\n```javascript\n// npm install axios\n\n// utils/api.ts\nimport axios from 'axios';\n\nconst apiClient = axios.create({\n baseURL: 'https://api.example.com',\n timeout: 10000,\n});\n\n// Перехватчик для добавления токена\napiClient.interceptors.request.use((config) => {\n const token = localStorage.getItem('token');\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Перехватчик для обработки ошибок\napiClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response?.status === 401) {\n // Токен истек, редирект на логин\n window.location.href = '/login';\n }\n return Promise.reject(error);\n }\n);\n\nexport default apiClient;\n\n// composables/useUsers.ts\nimport { ref } from 'vue';\nimport apiClient from '../utils/api';\n\nexport function useUsers() {\n const users = ref([]);\n const loading = ref(false);\n const error = ref(null);\n\n const fetchUsers = async () => {\n loading.value = true;\n try {\n const response = await apiClient.get('/users');\n users.value = response.data;\n } catch (err) {\n error.value = err.message;\n } finally {\n loading.value = false;\n }\n };\n\n const createUser = async (userData) => {\n try {\n const response = await apiClient.post('/users', userData);\n users.value.push(response.data);\n return response.data;\n } catch (err) {\n error.value = err.message;\n throw err;\n }\n };\n\n const updateUser = async (id, userData) => {\n try {\n const response = await apiClient.patch(`/users/${id}`, userData);\n const index = users.value.findIndex((u) => u.id === id);\n if (index !== -1) {\n users.value[index] = response.data;\n }\n return response.data;\n } catch (err) {\n error.value = err.message;\n throw err;\n }\n };\n\n const deleteUser = async (id) => {\n try {\n await apiClient.delete(`/users/${id}`);\n users.value = users.value.filter((u) => u.id !== id);\n } catch (err) {\n error.value = err.message;\n throw err;\n }\n };\n\n return { users, loading, error, fetchUsers, createUser, updateUser, deleteUser };\n}\n```\n\n## 3. Кэширование результатов запросов\n\n```javascript\n// composables/useCachedApi.ts\nimport { ref } from 'vue';\n\nconst cache = new Map();\n\nexport function useCachedApi() {\n const fetchData = async (url, options = {}) => {\n // Проверить кэш\n if (cache.has(url) && !options.skipCache) {\n return cache.get(url);\n }\n\n const response = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${localStorage.getItem('token')}`,\n ...options.headers,\n },\n ...options,\n });\n\n const data = await response.json();\n \n // Сохранить в кэш\n cache.set(url, data);\n \n // Очистить кэш через определенное время\n if (options.cacheTime) {\n setTimeout(() => cache.delete(url), options.cacheTime);\n }\n\n return data;\n };\n\n const clearCache = () => cache.clear();\n\n return { fetchData, clearCache };\n}\n```\n\n## 4. Обработка ошибок и retry логика\n\n```javascript\n// composables/useRetryApi.ts\nexport function useRetryApi() {\n const fetchWithRetry = async (url, options = {}, maxRetries = 3) => {\n let lastError;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${localStorage.getItem('token')}`,\n ...options.headers,\n },\n ...options,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n return await response.json();\n } catch (error) {\n lastError = error;\n \n // Не повторяй при 4xx ошибках (кроме 429)\n if (error.status >= 400 && error.status < 500 && error.status !== 429) {\n throw error;\n }\n\n // Экспоненциальная задержка перед повтором\n const delay = Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n };\n\n return { fetchWithRetry };\n}\n```\n\n## 5. Обработка загрузки файлов\n\n```javascript\n// composables/useFileUpload.ts\nexport function useFileUpload() {\n const uploadFile = async (file, url) => {\n const formData = new FormData();\n formData.append('file', file);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${localStorage.getItem('token')}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error('Upload failed');\n }\n\n return await response.json();\n } catch (error) {\n console.error('Upload error:', error);\n throw error;\n }\n };\n\n return { uploadFile };\n}\n```\n\n## 6. Real-time обновления с WebSocket\n\n```javascript\n// composables/useWebSocket.ts\nimport { ref } from 'vue';\n\nexport function useWebSocket(url) {\n const data = ref(null);\n const connected = ref(false);\n const ws = ref(null);\n\n const connect = () => {\n ws.value = new WebSocket(url);\n\n ws.value.onopen = () => {\n connected.value = true;\n };\n\n ws.value.onmessage = (event) => {\n data.value = JSON.parse(event.data);\n };\n\n ws.value.onerror = (error) => {\n console.error('WebSocket error:', error);\n };\n\n ws.value.onclose = () => {\n connected.value = false;\n };\n };\n\n const send = (message) => {\n if (ws.value?.readyState === WebSocket.OPEN) {\n ws.value.send(JSON.stringify(message));\n }\n };\n\n const disconnect = () => {\n ws.value?.close();\n };\n\n return { data, connected, connect, send, disconnect };\n}\n```\n\n## 7. Полный пример компонента с API\n\n```javascript\n\n\n\n\n\n```\n\n## Best Practices\n\n1. **Используй composables** для логики API\n2. **Абстрагируй HTTP клиент** (Axios или Fetch wrapper)\n3. **Обрабатывай ошибки глобально** через перехватчики\n4. **Кэшируй данные** где необходимо\n5. **Используй proper HTTP методы**: GET, POST, PATCH, DELETE\n6. **Валидируй данные** перед отправкой и после получения\n7. **Добавь timeout** для запросов\n8. **Логируй ошибки** для отладки\n\nСоблюдение этих практик обеспечит надежное и масштабируемое взаимодействие с backend в Vue приложениях.","dateCreated":"2026-04-03T17:55:55.755500","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Какую из задач с одинаковым приоритетам выберешь первой?

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

  1. Используй composables для логики API
  2. Абстрагируй HTTP клиент (Axios или Fetch wrapper)
  3. Обрабатывай ошибки глобально через перехватчики
  4. Кэшируй данные где необходимо
  5. Используй proper HTTP методы: GET, POST, PATCH, DELETE
  6. Валидируй данные перед отправкой и после получения
  7. Добавь timeout для запросов
  8. Логируй ошибки для отладки

Соблюдение этих практик обеспечит надежное и масштабируемое взаимодействие с backend в Vue приложениях.

Какую из задач с одинаковым приоритетам выберешь первой? | PrepBro