Pattern chuẩn để fetch async data trong Vue 3 Composition API là dùng onMounted với try/catch/finally, hoặc trích xuất thành composable tái sử dụng.
vue
<script setup>
import { ref, onMounted } from 'vue'
const users = ref([])
const loading = ref(true)
const error = ref(null)
// Pattern 1: onMounted (phổ biến nhất)
onMounted(async () => {
try {
const res = await fetch('/api/users')
users.value = await res.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
})
// Pattern 2: Composable reusable
// useFetch.ts — tái sử dụng ở nhiều components
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<ul v-else>
<li v-for="u in users" :key="u.id">{{ u.name }}</li>
</ul>
</template>Pitfall: không gọi async setup() trực tiếp mà không có <Suspense> bọc ngoài — dùng onMounted hoặc composable thay.
Với Nuxt: dùng useFetch() / useAsyncData() để SSR-compatible.