📖 Vue.js Toàn tập - State Management với Pinia
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

📝 Bài tập (1)

  1. Tái cấu trúc Todo App sử dụng Pinia cho state management

Tiến độ khóa học
Bài học "State Management với Pinia" - Khóa học "Vue.js Toàn tập"