Khi nào nên dùng Vuex / Pinia thay vì quản lý state thông thường?

Tạo bởi Hoàng Vũ, chỉnh sửa cuối lúc 13 tháng 3, 2025

Khi phát triển ứng dụng Vue.js, có nhiều cách để quản lý state, như sử dụng ref(), reactive(), hoặc các thư viện quản lý state như Vuex / Pinia. Nhưng khi nào nên dùng Vuex / Pinia thay vì quản lý state thông thường?

Mục tiêu bài học:

  • Hiểu khi nào nên dùng Vuex / Pinia thay vì ref()reactive().
  • Biết các trường hợp thực tế cần sử dụng Vuex / Pinia.
  • Hiểu về hiệu suất và tối ưu hóa khi làm việc với Vuex / Pinia.
  • Thực hành: Tích hợp Vuex / Pinia vào một ứng dụng CRUD thực tế.

Vuex / Pinia hay state thông thường?

1. Khi nào nên dùng Vuex / Pinia thay vì ref()reactive()?

Sử dụng ref()reactive() trong Pinia/Vue 3 phù hợp với:

  • Component nhỏ, đơn giản: Dữ liệu không cần chia sẻ giữa nhiều Component.
  • State cục bộ (local state): Chỉ được sử dụng trong một Component hoặc một nhánh nhỏ của ứng dụng.

Ví dụ: Quản lý state đơn giản với ref()

<script setup>
import { ref } from 'vue';

const count = ref(0);
const increment = () => count.value++;
</script>

<template>
  <button @click="increment">Tăng: {{ count }}</button>
</template>

Sử dụng Vuex / Pinia khi:

  • State cần chia sẻ giữa nhiều Component.
  • Dữ liệu phức tạp như danh sách sản phẩm, người dùng, giỏ hàng,....
  • Có nhiều thao tác cập nhật state từ các nơi khác nhau.
  • Cần lưu trữ dữ liệu lâu dài hoặc đồng bộ với server.

Ví dụ: Dùng Pinia để quản lý danh sách sản phẩm

// store/products.js
import { defineStore } from "pinia";

export const useProductStore = defineStore("product", {
  state: () => ({
    products: []
  }),
  actions: {
    async fetchProducts() {
      const response = await fetch("https://api.example.com/products");
      this.products = await response.json();
    }
  }
});

Kết luận: Nếu dữ liệu chỉ dùng trong một Component → Dùng ref()/reactive(). Nếu cần chia sẻ state hoặc quản lý dữ liệu phức tạp → Dùng Vuex / Pinia.

2. Trường hợp sử dụng Vuex / Pinia trong dự án thực tế

Dưới đây là những trường hợp thực tế khi Vuex / Pinia thực sự hữu ích:

Tình huống Quản lý state cục bộ (ref(), reactive()) Vuex / Pinia
Form đơn giản Không
Giỏ hàng (Shopping Cart) Không
Xác thực người dùng (Auth State) Không
Thông báo toàn cục (Global Notifications) Không
Danh sách sản phẩm Không
Bộ lọc & tìm kiếm dữ liệu Không

Ví dụ: Khi sử dụng Vuex / Pinia trong quản lý giỏ hàng

  • Khi một sản phẩm được thêm vào giỏ hàng, nhiều Component (navbar, danh sách giỏ hàng, trang thanh toán, v.v.) cần cập nhật state ngay lập tức.
  • Nếu dùng ref() hoặc reactive(), chúng ta phải truyền dữ liệu giữa nhiều Component bằng propsemit(), gây khó khăn trong bảo trì.
  • Vuex / Pinia giúp quản lý state tập trung, dễ mở rộng và tối ưu hiệu suất.

3. Hiệu suất và tối ưu hóa khi làm việc với Vuex / Pinia

Một số cách giúp tối ưu hiệu suất khi sử dụng Vuex / Pinia:

Sử dụng Getters thay vì tính toán lại mỗi lần render

// store/products.js
export const useProductStore = defineStore("product", {
  state: () => ({
    products: []
  }),
  getters: {
    totalProducts: (state) => state.products.length
  }
});

Lợi ích: Vue chỉ cập nhật lại khi state.products thay đổi, giúp giảm render không cần thiết.

Tận dụng persistedState để lưu trữ state

  • Để giữ lại state sau khi reload trang, có thể dùng pinia-plugin-persistedstate hoặc vuex-persistedstate.
// store/cart.js (Pinia)
import { defineStore } from 'pinia';

export const useCartStore = defineStore("cart", {
  state: () => ({
    cart: []
  }),
  persist: true // Lưu trữ giỏ hàng vào Local Storage
});

Sử dụng actions thay vì mutations trong Pinia

  • Pinia không cần mutations, giúp code gọn hơn và hiệu suất tốt hơn so với Vuex.

Chỉ load module khi cần thiết (Vuex Dynamic Modules)

const store = new Vuex.Store({});
store.registerModule('cart', cartModule); // Chỉ tải khi cần

Thực hành: Tích hợp Vuex / Pinia vào một ứng dụng CRUD thực tế

Yêu cầu:

  • Tạo một ứng dụng quản lý danh sách công việc (To-Do List).
  • Dữ liệu được lưu trữ trong Pinia / Vuex.
  • Cho phép thêm, sửa, xóa công việc.

Bước 1: Tạo store quản lý công việc (Pinia)

// store/todo.js
import { defineStore } from 'pinia';

export const useTodoStore = defineStore("todo", {
  state: () => ({
    todos: []
  }),
  actions: {
    addTodo(todo) {
      this.todos.push(todo);
    },
    removeTodo(index) {
      this.todos.splice(index, 1);
    }
  }
});

Bước 2: Tạo Component sử dụng Pinia

<template>
  <div>
    <h2>To-Do List</h2>
    <input v-model="newTask" placeholder="Nhập công việc..." />
    <button @click="addTodo(newTask)">Thêm</button>
    <ul>
      <li v-for="(todo, index) in todos" :key="index">
        {{ todo }}
        <button @click="removeTodo(index)">Xóa</button>
      </li>
    </ul>
  </div>
</template>

<script>
import { useTodoStore } from '../store/todo';
import { storeToRefs } from 'pinia';

export default {
  setup() {
    const store = useTodoStore();
    const { todos } = storeToRefs(store);
    const newTask = ref("");

    return {
      todos,
      newTask,
      addTodo: store.addTodo,
      removeTodo: store.removeTodo
    };
  }
};
</script>

Kết luận

Tóm tắt kiến thức quan trọng:

  • Dùng ref()/reactive() khi state chỉ dùng trong 1 Component.
  • Dùng Vuex / Pinia khi cần chia sẻ state giữa nhiều Component.
  • Vuex / Pinia tối ưu hiệu suất nhờ Getters, Persisted State, và Actions.
  • Thực hành: Tích hợp Vuex / Pinia vào ứng dụng CRUD.
Website Logo

Với hơn 10 năm kinh nghiệm lập trình web và từng làm việc với nhiều framework, ngôn ngữ như PHP, JavaScript, React, jQuery, CSS, HTML, CakePHP, Laravel..., tôi hy vọng những kiến thức được chia sẻ tại đây sẽ hữu ích và thiết thực cho các bạn.

Bình luận

Website Logo

Chào, tôi là Vũ. Đây là blog hướng dẫn lập trình của tôi.

Liên hệ công việc qua email dưới đây.

lhvuctu@gmail.com

Chúng Tôi Trên

Bạn đang muốn học về lập trình website?

Bạn cần nâng cao kiến thức chuyên nghiệp hơn để nâng cao cơ hội nghề nghiệp? Liên hệ