
Một ứng dụng thực tế không thể thiếu việc giao tiếp với backend. Trong Vue.js 3, chúng ta có thể dùng Fetch API (native) hoặc Axios (thư viện phổ biến).
Trong bài này, mình sẽ hướng dẫn:
-
Gọi API với Axios
-
Quản lý Loading & Error
-
Tổ chức API service riêng
-
Tích hợp với Pinia (lưu dữ liệu vào store)
-
Thêm Pagination & Infinite Scroll
1. Cài Axios
npm install axios
Tạo file src/api/axios.js
để cấu hình:
import axios from 'axios' const api = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com', // API giả lập timeout: 5000, }) export default api
2. Gọi API trong Component (cơ bản)
Ví dụ trong App.vue
gọi API lấy danh sách posts:
<template> <div class="app"> <h1>📡 API Example</h1> <button @click="fetchPosts">Load Posts</button> <p v-if="loading">Loading...</p> <p v-if="error" style="color:red">{{ error }}</p> <ul> <li v-for="post in posts" :key="post.id">{{ post.title }}</li> </ul> </div> </template> <script setup> import { ref } from 'vue' import api from './api/axios' const posts = ref([]) const loading = ref(false) const error = ref(null) async function fetchPosts() { loading.value = true error.value = null try { const res = await api.get('/posts?_limit=5') posts.value = res.data } catch (err) { error.value = 'Lỗi khi gọi API' } finally { loading.value = false } } </script>
👉 Đây là cách cơ bản: có loading
, error
, và hiển thị danh sách.
3. Tổ chức API service riêng
Thay vì gọi trực tiếp trong component, ta nên tách ra service.
Tạo src/api/postService.js
:
import api from './axios' export const postService = { getPosts(limit = 5, page = 1) { return api.get(`/posts?_limit=${limit}&_page=${page}`) }, getPostById(id) { return api.get(`/posts/${id}`) }, }
4. Tích hợp với Pinia
Tạo store src/stores/post.js
:
import { defineStore } from 'pinia' import { postService } from '../api/postService' export const usePostStore = defineStore('post', { state: () => ({ posts: [], loading: false, error: null, page: 1, }), actions: { async fetchPosts(limit = 5) { this.loading = true this.error = null try { const res = await postService.getPosts(limit, this.page) this.posts = res.data } catch (err) { this.error = 'Lỗi khi tải posts' } finally { this.loading = false } }, async nextPage(limit = 5) { this.page++ await this.fetchPosts(limit) }, }, })
Trong App.vue
:
<template> <div> <h1>📑 Post List</h1> <button @click="postStore.fetchPosts()">Load Posts</button> <p v-if="postStore.loading">Loading...</p> <p v-if="postStore.error" style="color:red">{{ postStore.error }}</p> <ul> <li v-for="post in postStore.posts" :key="post.id">{{ post.title }}</li> </ul> <button @click="postStore.nextPage()" :disabled="postStore.loading"> Next Page </button> </div> </template> <script setup> import { usePostStore } from './stores/post' const postStore = usePostStore() </script>
5. Infinite Scroll (vô tận)
Thêm vào store một hàm loadMore
:
async loadMore(limit = 5) { this.page++ this.loading = true try { const res = await postService.getPosts(limit, this.page) this.posts = [...this.posts, ...res.data] // nối thêm vào list } catch (err) { this.error = 'Lỗi khi tải thêm posts' } finally { this.loading = false } }
Trong component, dùng @scroll
hoặc thư viện như vueuse
để bắt sự kiện khi kéo gần cuối trang rồi gọi postStore.loadMore()
.
🎯 Tổng kết
-
Dùng Axios để gọi API, quản lý
loading
,error
. -
Nên tách API thành service để dễ quản lý.
-
Tích hợp Pinia để lưu dữ liệu tập trung.
-
Hỗ trợ phân trang và infinite scroll.