Lifecycle and Data Requests
In this chapter, you will learn Vue3 lifecycle hooks and use fetch to load blog data from a JSON file at the right time.
What is a Lifecycle?
Every Vue component goes through a series of stages from creation to destruction; these stages are called the "lifecycle."
Vue provides correspondinghook functionsat each stage, allowing you to execute code at specific points in time.
For example, if you want to request data after the component finishes loading, you need to useonMountedthis hook.
Vue3 Lifecycle Hooks Overview
| Hook Function | Execution Timing | Common Use Cases |
|---|---|---|
| setup() | Component initialization, executes the earliest | Define reactive data and computed properties |
| onBeforeMount | Before the component is mounted to the DOM | Less commonly used |
| onMounted | After the component is mounted to the DOM | Make network requests, initialize third-party libraries, get DOM references |
| onBeforeUpdate | Before data changes and DOM updates | Less commonly used |
| onUpdated | After data changes and DOM updates | Operations after DOM updates |
| onBeforeUnmount | Before the component is destroyed | Clear timers, cancel subscriptions, remove event listeners |
| onUnmounted | After the component is destroyed | Confirm cleanup work |
For the blog project, the one we use most often isonMounted—fetching article data after the page loads.
Lifecycle hooks must be written in
<script setup>setup, and cannot be placed inside async functions or conditional statements. Vue determines which component they belong to based on the order of the calls.
Basic Usage of onMounted
Example
import { ref, onMounted } from 'vue'
const message = ref('Loading...')
// onMounted executes after the component is mounted to the page
onMounted(() => {
// At this point, the DOM has finished rendering, so it is safe to manipulate the DOM or send requests.
console.log('Component has mounted, can start loading data now')
message.value = 'Data loading complete!'
})
</script>
<template>
<p>{{ message }}</p>
</template>
A component can call onMounted multiple times, and they will execute in registration order.
Loading JSON Data with fetch
We will put the article data in a JSON file, then use fetch in onMounted to load it.
Step 1: Create a Data File
Example
[
{
"id": 1,
"title": "Vue3 Complete Beginner's Guide",
"summary": "fromzerostart学习 Vue3 combinationformula API,涵盖 ref、reactive、computed waitCore Concepts。",
"content": "<h2>is什么学 Vue3?</h2><p>Vue3 Yes目before最flowOf the lineFrontend frameworkOne of...</p>",
"category": "Vue",
"date": "2024-05-10"
},
{
"id": 2,
"title": "JavaScript Asynchronous Programming Explained"
"summary": "One article to understand Promise, async/await, event loop, and microtask queue.",
"content": "<h2>What is asynchronous?</h2><p>JS is single-threaded...</p>",
"category": "JavaScript",
"date": "2024-05-08"
},
{
"id": 3,
"title": "CSS Grid Layout in Action"
"summary": "Easily implement complex responsive layouts with CSS Grid.",
"content": "<h2>Getting Started with Grid</h2><p>Grid is a two-dimensional layout system...</p>",
"category": "CSS",
"date": "2024-05-05"
}
]
The JSON file is placed in the
public/directory. Vite will serve it directly as a static file. In the browser, you can access it via/posts.jsondirectly. Files placed in public are not compiled; they are copied as-is to the build output.
Step 2: Fetch Data in the Component
Example
<script setup>
import { ref, onMounted } from 'vue'
import BlogCard from '../components/BlogCard.vue'
import CategoryFilter from '../components/CategoryFilter.vue'
const articles = ref([]) // initial empty array
const isLoading = ref(true) // loading state
const error = ref(null) // error message
// Asynchronously load article data
async function fetchPosts() {
isLoading.value = true
error.value = null
try {
const response = await fetch('/posts.json')
// Check whether the response is successful (throws an error on 404 or 500)
if (!response.ok) {
throw new Error(`Load failed: HTTP ${response.status}`)
}
const data = await response.json()
articles.value = data
} catch (err) {
error.value = err.message
console.error('Failed to load article data:', err)
} finally {
isLoading.value = false
}
}
// Load data immediately after the component is mounted
onMounted(() => {
fetchPosts()
})
</script>
<template>
<div class="home">
<h2>Latest Articles</h2>
<!-- Loading state -->
<p v-if="isLoading" class="status-msg">Loading, please wait...</p>
<!-- Error state -->
<p v-else-if="error" class="status-msg error">
Load failed: {{ error }}
<button @click="fetchPosts">Retry</button>
</p>
<!-- Empty data state -->
<p v-else-if="articles.length === 0" class="status-msg">
No articles yet, please stay tuned.
</p>
<!-- Normal data display -->
<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>
Handling Three States of Async Requests
Every data request should cover the following three UI states, otherwise the user experience will be poor:
| State | When It Appears | UI Representation |
|---|---|---|
| Loading | After the request is sent, before the response returns | Loading animation, skeleton screen |
| Success | Data is returned correctly | Render content normally |
| Failure | Network error, server exception | Error message + retry button |
You can also handle a fourth state—empty data(the request succeeds but returns an empty array), giving users a clear message instead of a blank page.
Hands-on: Also Switch the Detail Page to fetch Loading
The detail page needs to find the corresponding article from the JSON based on the ID in the URL.
You can reuse the data loading logic—also call fetch on the detail page, then filter.
Example
<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([]) // all posts
const isLoading = ref(true)
const error = ref(null)
// Find current article by ID
const article = computed(() => {
return posts.value.find(p => p.id === id)
})
// Load all article data
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">Loading...</p>
<p v-else-if="error">Load failed: {{ error }}</p>
<div v-else-if="!article">
<h2>Article does not exist</h2>
<RouterLink to="/">Back to Home</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="/">← Back to Home</RouterLink>
</article>
</div>
</template>
Chapter Summary
In this chapter, you mastered the most essential Vue3 lifecycle hook, onMounted, used fetch to load JSON data from the public directory, and learned how to handle the four UI states of a request (loading/success/failure/empty).
Now the blog data is loaded asynchronously from an external JSON file, which is closer to how real projects work.
Other Extensions