75 phút
State Management với Pinia
Giới thiệu Pinia
Pinia là state management library chính thức cho Vue.js.
Cài đặt và Cấu hình
Cài đặt
npm install pinia
Cấu hình
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
Tạo Store
Option Stores
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'My Counter'
}),
getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlusOne() {
return this.doubleCount + 1
}
},
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
async incrementAsync() {
await new Promise(resolve => setTimeout(resolve, 1000))
this.increment()
}
}
})
Setup Stores
// stores/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
const user = ref(null)
const isAuthenticated = computed(() => user.value !== null)
function login(userData) {
user.value = userData
}
function logout() {
user.value = null
}
return {
user,
isAuthenticated,
login,
logout
}
})
Sử dụng Store trong Components
Composition API
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
// Sử dụng storeToRefs để giữ reactivity
const { count, doubleCount } = storeToRefs(counterStore)
const { increment, decrement } = counterStore
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
Options API
<script>
import { mapState, mapActions } from 'pinia'
import { useCounterStore } from '@/stores/counter'
export default {
computed: {
...mapState(useCounterStore, ['count', 'doubleCount'])
},
methods: {
...mapActions(useCounterStore, ['increment', 'decrement'])
}
}
</script>
Advanced Patterns
Multiple Stores
<script setup>
import { useCounterStore } from '@/stores/counter'
import { useUserStore } from '@/stores/user'
const counterStore = useCounterStore()
const userStore = useUserStore()
// Truy cập multiple stores
const combinedData = computed(() => ({
count: counterStore.count,
userName: userStore.user?.name
}))
</script>
Store Subscriptions
// Theo dõi state changes
counterStore.$subscribe((mutation, state) => {
console.log('State changed:', mutation, state)
})
// Theo dõi action calls
counterStore.$onAction(({ name, store, args, after, onError }) => {
console.log('Action called:', name)
after((result) => {
console.log('Action finished:', name, result)
})
onError((error) => {
console.error('Action failed:', name, error)
})
})
Persistence với Pinia Plugin
// plugins/persistence.js
import { createPinia } from 'pinia'
const pinia = createPinia()
pinia.use(({ store }) => {
// Khôi phục state từ localStorage
const savedState = localStorage.getItem(store.$id)
if (savedState) {
store.$patch(JSON.parse(savedState))
}
// Lưu state khi có thay đổi
store.$subscribe((mutation, state) => {
localStorage.setItem(store.$id, JSON.stringify(state))
})
})
export default pinia