Vuex là một thư viện quản lý trạng thái chính thức cho Vue.js, giúp tập trung hóa dữ liệu của toàn bộ ứng dụng vào một nơi duy nhất (Store). Điều này đặc biệt hữu ích cho các ứng dụng có quy mô lớn, nơi việc truyền dữ liệu qua lại giữa các component trở nên phức tạp.
1. Cấu hình Store chính
Đầu tiên, chúng ta khởi tạo Vuex Store bằng cách sử dụng Vue.use(Vuex). Trong file cấu hình chính (thường là store/index.js hoặc index.ts), chúng ta sẽ kết hợp các module lại với nhau.
import Vue from 'vue';
import Vuex from 'vuex';
import accountModule from './modules/account';
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
account: accountModule
}
});
2. Tích hợp Store vào ứng dụng
Để Store có thể truy cập được từ mọi component thông qua this.$store, bạn cần khai báo nó khi khởi tạo instance Vue chính.
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
store,
render: h => h(App)
}).$mount('#app');
3. Định nghĩa Module với Namespace
Sử dụng Module giúp chia nhỏ Store thành các phần riêng biệt. Dưới đây là ví dụ về một module quản lý thông tin người dùng sử dụng TypeScript để định nghĩa kiểu dữ liệu.
import type { Module } from 'vuex';
interface IUserStatus {
email: string;
points: number;
}
const accountModule: Module<IUserStatus, any> = {
namespaced: true,
state: {
email: '',
points: 0
},
mutations: {
SET_EMAIL(state, payload: string) {
state.email = payload;
},
SET_POINTS(state, payload: number) {
state.points = payload;
}
},
getters: {
userEmail: state => state.email,
userPoints: state => state.points
},
actions: {
updateEmailAsync({ commit }, newEmail: string) {
// Giả lập logic bất đồng bộ
setTimeout(() => {
commit('SET_EMAIL', newEmail);
}, 500);
},
updatePointsAsync({ commit }, newPoints: number) {
commit('SET_POINTS', newPoints);
}
}
};
export default accountModule;
4. Sử dụng Vuex trong Component
Vuex cung cấp các helper functions như mapState, mapGetters, mapActions, và mapMutations để rút ngắn mã nguồn khi làm việc với Store trong các component.
<template>
<div class="user-profile">
<h1>Thông tin tài khoản</h1>
<section>
<p>Email hiện tại: {{ emailFromStore }}</p>
<p>Điểm tích lũy: {{ pointsFromStore }}</p>
</section>
<hr>
<div>
<label>Cập nhật Email:</label>
<input v-model="localEmail" type="text" placeholder="Nhập email mới" />
<button @click="handleUpdateEmail">Xác nhận thay đổi</button>
</div>
<div>
<label>Thay đổi điểm:</label>
<input v-model.number="localPoints" type="number" />
<button @click="SET_POINTS(localPoints)">Cập nhật điểm (Sync)</button>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';
export default {
data() {
return {
localEmail: '',
localPoints: 0
};
},
computed: {
// Truy xuất state thông qua mapState
...mapState('account', {
emailFromStore: state => state.email,
pointsFromStore: state => state.points
}),
// Truy xuất thông qua getters
...mapGetters('account', ['userEmail', 'userPoints'])
},
methods: {
// Đăng ký các actions và mutations từ module 'account'
...mapActions('account', ['updateEmailAsync']),
...mapMutations('account', ['SET_POINTS']),
handleUpdateEmail() {
if (this.localEmail) {
// Gọi action để xử lý bất đồng bộ
this.updateEmailAsync(this.localEmail);
}
},
// Ví dụ về cách gọi trực tiếp không qua helper
manualUpdate() {
this.$store.dispatch('account/updateEmailAsync', 'test@example.com');
this.$store.commit('account/SET_POINTS', 100);
console.log(this.$store.state.account.email);
}
}
};
</script>
Việc sử dụng namespaced: true yêu cầu bạn phải thêm tên của module (trong trường hợp này là 'account') làm tham số đầu tiên trong các hàm helper hoặc thêm tiền tố vào đường dẫn khi gọi dispatch hoặc commit trực tiếp.