Skip to content

Buổi 12: Quản lý State với Pinia

🎯 Mục tiêu học tập

  • Giải thích được tại sao và khi nào cần sử dụng thư viện quản lý trạng thái toàn cục (State Management).
  • Trình bày được kiến trúc của Pinia gồm: Store, State, Getters và Actions.
  • Khởi tạo và cấu hình thành công Pinia trong ứng dụng Vue 3.
  • Xây dựng Store quản lý giỏ hàng E-Commerce toàn cục và liên kết với các component tương tác.

📖 Lý thuyết cốt lõi

1. Tại sao cần State Management?

  • Trong các ứng dụng lớn, dữ liệu cần được chia sẻ giữa các component không có mối quan hệ cha-con trực tiếp (ví dụ: số lượng giỏ hàng hiển thị trên Navbar nhưng nút "Thêm vào giỏ" nằm ở Product Card hay Trang chi tiết).
  • Pinia cung cấp một kho lưu trữ tập trung (Store) chứa các trạng thái toàn cục mà bất cứ component nào cũng có thể đọc và ghi trực tiếp.

2. Cấu trúc Store của Pinia (Cú pháp Setup Store)

  • State: Tương đương với dữ liệu khai báo qua ref() (Nơi lưu trữ data).
  • Getters: Tương đương với computed() (Tính toán dữ liệu dựa trên State).
  • Actions: Tương đương với function() (Hàm thay đổi State hoặc gọi API).
javascript
// src/stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }
  return { count, doubleCount, increment }
})

💻 Ví dụ thực tiễn

vue
<script setup>
import { useCounterStore } from '../stores/counter'
const counter = useCounterStore()
</script>

<template>
  <div>
    <p>Số đếm: {{ counter.count }}</p>
    <button @click="counter.increment">Tăng</button>
  </div>
</template>

🛠️ Bài tập thực hành (Lab)

Yêu cầu: Xây dựng Giỏ hàng toàn cục (Global Shopping Cart) bằng Pinia

  1. Cấu hình Pinia vào dự án thực hành.
  2. Tạo file src/stores/cart.js để định nghĩa useCartStore:
    • State: mảng cartItems (mỗi phần tử gồm id, name, price, image, quantity).
    • Getters:
      • totalPrice (tổng tiền giỏ hàng: cộng dồn price * quantity của từng item).
      • totalCount (tổng số lượng sản phẩm trong giỏ hàng).
    • Actions:
      • addToCart(product): thêm mới sản phẩm vào giỏ hoặc tự động tăng số lượng lên 1 nếu sản phẩm đã tồn tại.
      • removeFromCart(productId): xóa hẳn sản phẩm ra khỏi mảng cartItems.
      • updateQuantity(productId, type): tăng hoặc giảm số lượng của sản phẩm (nếu loại là 'decrease' thì giảm và chặn không cho nhỏ hơn 1).
  3. Tại component ProductCard.vue, liên kết nút "Thêm vào giỏ" (dòng 145-147 trong index.html) để gọi action addToCart.
  4. Tại component Navbar.vue, hiển thị số lượng giỏ hàng động trên icon giỏ hàng (dòng 58-60 của index.html) liên kết trực tiếp với getter totalCount.
  5. Kết nối các hành động tăng/giảm/xóa ở trang Giỏ hàng CartView.vue với các actions của useCartStore.

❓ Trắc nghiệm nhanh

1. Thư viện quản lý state toàn cục chính thức được khuyên dùng cho Vue 3 là gì?

  • A. Vuex
  • B. Redux
  • C. Pinia
  • D. MobX Đáp án đúng: C.

2. Thành phần nào trong Pinia đóng vai trò tương tự như computed properties trong component?

  • A. State
  • B. Getters
  • C. Actions
  • D. Mutations Đáp án đúng: B.

3. Để lấy dữ liệu state từ store mà vẫn giữ được tính phản ứng (Reactivity) khi destructure?

  • A. Dùng destructure bình thường: const { cartItems } = store.
  • B. Sử dụng hàm trợ giúp storeToRefs(store).
  • C. Dùng computed(store).
  • D. Không thể destructure. Đáp án đúng: B.

📝 Checklist hoàn thành

  • [ ] Cấu hình thành công Pinia store quản lý mảng giỏ hàng toàn cục.
  • [ ] Đồng bộ thành công số lượng giỏ hàng trên Navbar ngay khi click thêm sản phẩm ở trang chủ.
  • [ ] Các hành động tăng/giảm/xóa trên trang giỏ hàng liên kết trực tiếp với Pinia actions.

Released under the MIT License.