Khi nào nên dùng Vuex / Pinia thay vì quản lý state thông thường?
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()
và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()
và reactive()
?
Sử dụng ref()
và 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 | Có | Không |
Giỏ hàng (Shopping Cart) | Không | Có |
Xác thực người dùng (Auth State) | Không | Có |
Thông báo toàn cục (Global Notifications) | Không | Có |
Danh sách sản phẩm | Không | Có |
Bộ lọc & tìm kiếm dữ liệu | Không | Có |
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ặcreactive()
, chúng ta phải truyền dữ liệu giữa nhiều Component bằngprops
vàemit()
, 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ặcvuex-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.

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.
Xem thêm

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