Загрузка данных из API
Большинство приложений получают данные с сервера через API — адрес, который возвращает JSON. С этого урока курс переходит ко второму примеру — блогу, который читает статьи с тестового API JSONPlaceholder. Загрузка занимает время и может завершиться ошибкой, поэтому компонент должен показывать три состояния: «загружается», «ошибка» и сами данные.
Новый проект
Создайте проект блога так же, как список задач:
npm create vue@latestНазовите его blog, дополнительные возможности пока не отмечайте. Затем выполните cd blog, npm install, npm run dev.
Загрузка списка статей
Создайте src/components/PostList.vue:
<!-- src/components/PostList.vue -->
<script setup>
import { ref, onMounted } from 'vue'
const posts = ref([])
onMounted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')
posts.value = await response.json()
})
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>Подключите его в src/App.vue — <PostList /> после импорта. Через мгновение после открытия http://localhost:5173 появятся заголовки пяти статей. fetch и async/await работают так же, как в уроках учебника, а Vue перерисует список, когда в posts попадёт массив.
Загрузка и ошибка
Добавим два состояния — loading и error:
<!-- src/components/PostList.vue -->
<script setup>
import { ref, onMounted } from 'vue'
const posts = ref([])
const loading = ref(true)
const error = ref(null)
onMounted(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')
if (!response.ok) {
throw new Error(`Сервер ответил ${response.status}`)
}
posts.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
})
</script>
<template>
<p v-if="loading">Загрузка...</p>
<p v-else-if="error">Не удалось загрузить статьи: {{ error }}</p>
<ul v-else>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>Сначала на странице видно «Загрузка...», затем список. Чтобы проверить ошибку, поменяйте в адресе posts на postz — появится «Не удалось загрузить статьи: Сервер ответил 404».
Состояния компонента
| Состояние | loading | error | Что видно |
|---|---|---|---|
| Идёт запрос | true | null | «Загрузка...» |
| Ошибка | false | Текст ошибки | Сообщение об ошибке |
| Успех | false | null | Список статей |
Композабл для запросов
Одни и те же три переменные понадобятся на каждой странице блога. Вынесите их в src/composables/useFetch.js:
// src/composables/useFetch.js
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const loading = ref(true)
const error = ref(null)
fetch(url)
.then((response) => {
if (!response.ok) throw new Error(`Сервер ответил ${response.status}`)
return response.json()
})
.then((json) => (data.value = json))
.catch((e) => (error.value = e.message))
.finally(() => (loading.value = false))
return { data, loading, error }
}В компоненте остаётся одна строка: const { data: posts, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts?_limit=5').
Частая ошибка
Сделать асинхронным весь <script setup>, написав await fetch(...) на верхнем уровне. Такой компонент Vue выводит только внутри специального компонента Suspense, а без него страница останется пустой. Загружайте данные в onMounted или через композабл, как в этом уроке.