Жизненный цикл и запросы данных
В этой главе вы изучите хуки жизненного цикла Vue3 и научитесь использовать fetch для загрузки данных блога из JSON-файла в нужный момент.
Что такое жизненный цикл?
Каждый компонент проходит через ряд этапов от создания до уничтожения; эти этапы называются "жизненным циклом".
Vue предоставляет соответствующиефункции-хукина каждом этапе, позволяя вам выполнять код в определённые моменты времени.
Например, если вы хотите запросить данные после завершения загрузки компонента, вам нужно использоватьonMountedэтот хук.
Обзор хуков жизненного цикла Vue3
| Хук-функция | Время выполнения | Типичные случаи использования |
|---|---|---|
| setup() | Инициализация компонента, выполняется самым первым | Определение реактивных данных и вычисляемых свойств |
| onBeforeMount | Перед монтированием компонента в DOM | Используется реже |
| onMounted | После монтирования компонента в DOM | Выполнение сетевых запросов, инициализация сторонних библиотек, получение ссылок на DOM |
| onBeforeUpdate | Перед изменением данных и обновлением DOM | Используется реже |
| onUpdated | После изменения данных и обновления DOM | Операции после обновления DOM |
| onBeforeUnmount | Перед уничтожением компонента | Очистить таймеры, отменить подписки, удалить обработчики событий |
| onUnmounted | После уничтожения компонента | Подтвердить выполнение очистки |
В проекте блога чаще всего мы используемonMounted—получение данных статьи после загрузки страницы.
Хуки жизненного цикла должны быть написаны в
<script setup>setup, и не могут быть помещены внутрь асинхронных функций или условных операторов. Vue определяет, какому компоненту они принадлежат, по порядку вызовов.
Базовое использование onMounted
Пример
import { ref, onMounted } from 'vue'
const message = ref('Loading...')
// onMounted выполняется после монтирования компонента на страницу
onMounted(() => {
// На этом этапе DOM завершил рендеринг, поэтому можно безопасно манипулировать DOM или отправлять запросы.
console.log('Component has mounted, can start loading data now')
message.value = 'Data loading complete!'
})
</script>
<template>
<p>{{ message }}</p>
</template>
Компонент может вызывать onMounted несколько раз, и они будут выполняться в порядке регистрации.
Загрузка данных JSON с помощью fetch
Мы поместим данные статьи в файл JSON, затем используем fetch в onMounted для их загрузки.
Шаг 1: Создайте файл данных
Пример
[
{
"id": 1,
"title": "Vue3: полное руководство для начинающих",
"summary": "Изучение Vue3 с нуля: Composition API, включая ref, reactive, computed и основные концепции.",
"content": "<h2>Почему стоит изучать Vue3?</h2><p>Vue3 — один из самых популярных современных фронтенд-фреймворков...</p>",
"category": "Vue",
"date": "2024-05-10"
},
{
"id": 2,
"title": "Асинхронное программирование в JavaScript: подробный разбор"
"summary": "Одна статья, чтобы разобраться в Promise, async/await, событийном цикле и очереди микрозадач.",
"content": "<h2>Что такое асинхронность?</h2><p>JS однопоточный...</p>",
"category": "JavaScript",
"date": "2024-05-08"
},
{
"id": 3,
"title": "CSS Grid на практике"
"summary": "Легко реализуйте сложные адаптивные макеты с помощью CSS Grid.",
"content": "<h2>Начало работы с Grid</h2><p>Grid — это двумерная система компоновки...</p>",
"category": "CSS",
"date": "2024-05-05"
}
]
JSON-файл размещается в
public/каталоге. Vite будет отдавать его напрямую как статический файл. В браузере вы можете обратиться к нему по/posts.jsonнапрямую. Файлы, помещённые в public, не компилируются; они копируются как есть в результат сборки.
Шаг 2: Получение данных в компоненте
Пример
<script setup>
import { ref, onMounted } from 'vue'
import BlogCard from '../components/BlogCard.vue'
import CategoryFilter from '../components/CategoryFilter.vue'
const articles = ref([]) // изначально пустой массив
const isLoading = ref(true) // состояние загрузки
const error = ref(null) // сообщение об ошибке
// Асинхронно загрузить данные статей
async function fetchPosts() {
isLoading.value = true
error.value = null
try {
const response = await fetch('/posts.json')
// Проверить, успешен ли ответ (выбрасывает ошибку при 404 или 500)
if (!response.ok) {
throw new Error(`Ошибка загрузки: HTTP ${response.status}`)
}
const data = await response.json()
articles.value = data
} catch (err) {
error.value = err.message
console.error('Не удалось загрузить данные статьи:', err)
} finally {
isLoading.value = false
}
}
// Загрузить данные сразу после монтирования компонента
onMounted(() => {
fetchPosts()
})
</script>
<template>
<div class="home">
<h2>Последние статьи</h2>
<!-- Состояние загрузки -->
<p v-if="isLoading" class="status-msg">Загрузка, пожалуйста, подождите...</p>
<!-- Состояние ошибки -->
<p v-else-if="error" class="status-msg error">
Не удалось загрузить: {{ error }}
<button @click="fetchPosts">Повторить</button>
</p>
<!-- Состояние пустых данных -->
<p v-else-if="articles.length === 0" class="status-msg">
Пока нет статей, пожалуйста, следите за обновлениями.
</p>
<!-- Обычное отображение данных -->
<template v-else>
<div class="article-grid">
<BlogCard
v-for="article in articles"
:key="article.id"
:id="article.id"
:title="article.title"
:summary="article.summary"
:date="article.date"
:category="article.category"
/>
</div>
</template>
</div>
</template>
<style scoped>
.status-msg {
text-align: center;
padding: 60px 0;
font-size: 16px;
color: #999;
}
.error { color: #e74c3c; }
.error button {
margin-left: 10px;
padding: 4px 12px;
cursor: pointer;
}
</style>
Обработка трёх состояний асинхронных запросов
Каждый запрос данных должен охватывать следующие три состояния интерфейса, иначе пользовательский опыт будет плохим:
| State | Когда появляется | Представление в интерфейсе |
|---|---|---|
| Загрузка | После отправки запроса, до возврата ответа | Анимация загрузки, скелетон-экран |
| Успех | Данные возвращаются корректно | Обычный вывод содержимого |
| Failure | Сетевая ошибка, исключение сервера | Сообщение об ошибке + кнопка повтора |
Вы также можете обработать четвёртое состояние —пустые данные(запрос успешен, но возвращает пустой массив), давая пользователям понятное сообщение вместо пустой страницы.
Практика: также переключите страницу деталей на загрузку с fetch
Страница деталей должна найти соответствующую статью в JSON по ID из URL.
Вы можете переиспользовать логику загрузки данных — также вызвать fetch на странице деталей, а затем отфильтровать.
Пример
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
const posts = ref([]) // все посты
const isLoading = ref(true)
const error = ref(null)
// Найти текущую статью по ID
const article = computed(() => {
return posts.value.find(p => p.id === id)
})
// Загрузить все данные статьи
async function fetchPosts() {
isLoading.value = true
try {
const res = await fetch('/posts.json')
if (!res.ok) throw new Error('Failed to load')
posts.value = await res.json()
} catch (err) {
error.value = err.message
} finally {
isLoading.value = false
}
}
onMounted(() => {
fetchPosts()
})
</script>
<template>
<div class="post-view">
<p v-if="isLoading">Загрузка...</p>
<p v-else-if="error">Не удалось загрузить: {{ error }}</p>
<div v-else-if="!article">
<h2>Статья не существует</h2>
<RouterLink to="/">Назад на главную</RouterLink>
</div>
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<div class="content" v-html="article.content"></div>
<RouterLink to="/">← Назад на главную</RouterLink>
</article>
</div>
</template>
Краткое содержание главы
В этой главе вы освоили самый важный хук жизненного цикла Vue3, onMounted, использовали fetch для загрузки JSON-данных из каталога public и научились обрабатывать четыре состояния UI запроса (загрузка/успех/ошибка/пусто).
Теперь данные блога загружаются асинхронно из внешнего JSON-файла, что ближе к тому, как работают реальные проекты.
Другие расширения