一、Pinia 核心概念
Pinia?是 Vue3 官方推薦的狀態管理庫,相比 Vuex 4,具有以下優勢:
-
更簡潔的 API(移除?
mutations
) -
完整的 TypeScript 支持
-
支持組合式 API
-
自動代碼分割
-
輕量級(僅 1KB)
二、核心概念對比(Pinia vs Vuex)
特性 | Pinia | Vuex |
---|---|---|
狀態存儲 | defineStore() | new Vuex.Store() |
狀態定義 | state() ?函數 | state ?對象 |
數據修改 | 直接修改或?$patch | 必須通過?mutations |
異步操作 | 直接在?actions ?處理 | 通過?actions ?調用?mutations |
模塊系統 | 天然模塊化 | 需要手動分模塊 |
TypeScript | 一流支持 | 需要額外類型定義 |
三、基礎使用
1. 安裝與創建 Store
npm install pinia
// stores/counter.js
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {state: () => ({count: 0}),getters: {doubleCount: (state) => state.count * 2},actions: {increment() {this.count++},async fetchData() {const res = await fetch('/api/data')this.data = await res.json()}}
})
2. 掛載到 Vue 實例
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'const app = createApp(App)
app.use(createPinia())
app.mount('#app')
四、組件中使用 Store
1. 基礎使用(Options API)
<script>
import { useCounterStore } from '@/stores/counter'export default {setup() {const counter = useCounterStore()return { counter }},computed: {quadrupleCount() {return this.counter.doubleCount * 2}}
}
</script><template><div>{{ counter.count }}</div><button @click="counter.increment()">+1</button>
</template>
2. 組合式 API(推薦)
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'const counter = useCounterStore()// 解構保持響應式
const { count, doubleCount } = storeToRefs(counter)// 直接調用 action
function handleClick() {counter.increment()
}
</script>
五、核心功能詳解
1. State 管理
// 定義
const useStore = defineStore('storeId', {state: () => ({user: null,items: []})
})// 使用
const store = useStore()// 直接訪問
console.log(store.user)// 批量修改
store.$patch({user: { name: 'Alice' },items: [...store.items, newItem]
})// 重置狀態
store.$reset()
2. Getters 計算屬性
const useStore = defineStore('storeId', {state: () => ({ count: 0 }),getters: {double: (state) => state.count * 2,// 使用其他 getterquadruple() {return this.double * 2}}
})
3. Actions 操作
const useStore = defineStore('storeId', {actions: {async login(userData) {try {this.user = await api.login(userData)} catch (error) {throw new Error(error)}}}
})// 調用
store.login({ user: 'admin' }).catch(handleError)
六、高級功能
1. 訂閱狀態變化
const unsubscribe = store.$subscribe((mutation, state) => {console.log('狀態變化:', mutation.type)console.log('新狀態:', state)
})// 取消訂閱
unsubscribe()
2. 插件開發
// 持久化插件示例
const persistPlugin = ({ store }) => {const key = `pinia-${store.$id}`const savedState = localStorage.getItem(key)if (savedState) {store.$patch(JSON.parse(savedState))}store.$subscribe((_, state) => {localStorage.setItem(key, JSON.stringify(state))})
}// 使用插件
const pinia = createPinia()
pinia.use(persistPlugin)
3. 服務端渲染 (SSR)
// 服務端
export default {async setup() {const pinia = createPinia()const app = createApp(App).use(pinia)const initialState = JSON.stringify(pinia.state.value)return { initialState }}
}// 客戶端
if (window.__INITIAL_STATE__) {pinia.state.value = JSON.parse(window.__INITIAL_STATE__)
}
七、TypeScript 集成
1. 類型化 Store
interface UserState {name: stringage: number
}export const useUserStore = defineStore('user', {state: (): UserState => ({name: 'Alice',age: 25}),getters: {isAdult: (state) => state.age >= 18}
})
2. 類型安全的插件
import { PiniaPluginContext } from 'pinia'declare module 'pinia' {export interface PiniaCustomProperties {$hello: (msg: string) => string}
}const helloPlugin = ({ store }: PiniaPluginContext) => {store.$hello = (msg: string) => `Hello ${msg}!`
}
八、最佳實踐
-
Store 組織規范
src/
├── stores/
│ ├── modules/
│ │ ├── user.store.ts
│ │ └── cart.store.ts
│ └── index.ts # 統一導出
-
模塊化 Store
// stores/modules/user.store.ts
export const useUserStore = defineStore('user', { /* ... */ })// stores/index.ts
export * from './modules/user.store'
export * from './modules/cart.store'
-
組合 Store
// 組合多個 Store
const useCombinedStore = () => {const user = useUserStore()const cart = useCartStore()const totalPrice = computed(() => user.isVIP ? cart.total * 0.9 : cart.total)return { user, cart, totalPrice }
}
-
性能優化
// 避免重復訂閱
const store = useStore()
const doubleCount = computed(() => store.doubleCount)
九、常見問題解決方案
Q1: 如何調試?
安裝?Vue Devtools,支持直接查看和修改 Pinia 狀態
Q2: 如何與 Vuex 共存?
// 在 Vue3 項目中可以同時使用
import { createPinia } from 'pinia'
import { createStore } from 'vuex'const pinia = createPinia()
const vuexStore = createStore({ /* ... */ })app.use(pinia)
app.use(vuexStore)
Q3: 如何持久化存儲?
使用官方推薦插件?pinia-plugin-persistedstate
:
npm install pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)// Store 配置
defineStore('user', {persist: {key: 'my-custom-key',paths: ['userInfo']}
})
Q4: 如何重置單個狀態?
const store = useStore()// 方法一:直接賦值
store.user = null// 方法二:使用 $patch
store.$patch({ user: null })// 方法三:重置全部
store.$reset()
十、總結
Pinia 適用場景:
-
需要 TypeScript 深度支持的項目
-
希望簡化狀態管理流程的新項目
-
需要模塊化狀態管理的復雜應用
-
需要與服務端渲染(SSR)集成的項目