哈嘍,各位小夥伴,歡迎來到我是wangfang呀的博客!我是我是wangfang呀,雖然還在編程的“菜鳥”階段,但我已經迫不及待地想和大家分享我一路上踩過的坑和學到的小技巧。如果你也曾為bug頭疼,那麼你來對地方了!今天的內容希望能夠給大家帶來一些靈感和幫助。
前言
Pinia = Vue Core Team 針對 Vue 3 重新思考後的狀態管理庫
- 更輕、更 TypeScript-friendly
- 與 Composition API 無縫融合
- 依舊保持 DevTools 時間旅行、熱重載 等“Vuex 式”體驗
本文聚焦四個維度: 1. 設計理念 2. 與 Vuex 的異同 3. Store + 組合式 API 4. 插件開發 & 數據持久化
1 | Pinia 的設計理念
| 關鍵詞 | 釋義 |
|---|---|
| 直觀 | 拋棄“Mutation 必經之路”——直接在 state 上改數據 |
| 輕量 | 核心 <2 kB gzip;API 極少、學習成本低 |
| 組合式 | defineStore + setup() = 天然 Composition API |
| TS 零配置 | 類型自動推導 (state / getters / actions→this) |
| DevTools 同級 | 時間旅行、快照、HMR 與 Vuex 一樣一個都不少 |
2 | Vuex vs Pinia:對比速覽
| 維度 | Vuex 3/4 | Pinia |
|---|---|---|
| 核心概念 | state / getter / mutation / action / module | state / getter / action(mutation 被合併進 action 或直接改 state) |
| 修改方式 | 必須 mutation → commit |
直接 this.count++ 或 this.$patch() |
| TS 體驗 | 需手寫泛型 & 輔助類型 | 自動推斷,幾乎零泛型 |
| 依賴 Vue 版本 | Vue 2.x→Vuex3,Vue 3→Vuex4 | Vue 3 原生;Vue 2 亦可 + composition-api |
| API 風格 | Options API | Composition API / Options 均可 |
| 插件系統 | store.subscribe、自定義插件 |
等價,且寫法更簡單 |
| 學習成本 | 較高 | 低 |
3 | Store 定義與 Composition API 集成
3.1 安裝與初始化
npm i pinia # 或 pnpm add pinia
// main.ts
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).use(createPinia()).mount('#app')
3.2 一個最小 Store:useCounter()
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounter = defineStore('counter', {
state: () => ({
count: 0,
name: 'VueConf'
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
async fetchInit() {
const { data } = await api.get('/count')
this.count = data
}
}
})
- id 字符串
'counter'作為 DevTools & SSR keythis自動類型推斷:this.count、this.double均有 IntelliSense- 在組件/Composables 中 直接修改 state;無需 “mutation 層”
3.3 組件中使用
<script setup lang="ts">
import { useCounter } from '@/stores/counter'
const counter = useCounter()
</script>
<template>
<button @click="counter.increment">+1</button>
<p>{{ counter.count }} → {{ counter.double }}</p>
</template>
- 多處導入同一 store 是 同一個實例
- 支持解構:
const { count } = storeToRefs(counter)(保持響應式)
3.4 setup Store:更自由的組合式寫法
export const useCart = defineStore('cart', () => {
const items = ref<CartItem[]>([])
const total = computed(() => items.value.reduce((sum, i) => sum + i.price, 0))
function add(item: CartItem) {
items.value.push(item)
}
return { items, total, add }
})
- 完全等同 Setup 組件語法
- 行為/狀態寫在一起,邏輯更集中
4 | 插件開發 & 數據持久化
4.1 全局插件
// pinia-plugin-log.ts
import { PiniaPluginContext } from 'pinia'
export function LoggerPlugin({ store }: PiniaPluginContext) {
store.$subscribe((_mutation, state) => {
console.log(`[${store.$id}] ->`, JSON.parse(JSON.stringify(state)))
})
}
const pinia = createPinia()
pinia.use(LoggerPlugin)
$subscribe監聽 每次state變化(包括深層 patch)- 也可用
$onAction監聽 action 調用
4.2 本地持久化(LocalStorage)
function PersistPlugin({ store }) {
const key = `__persist__${store.$id}`
// 初始化:讀緩存
const cache = localStorage.getItem(key)
if (cache) store.$patch(JSON.parse(cache))
// 訂閲變化:寫緩存
store.$subscribe((_mutation, state) => {
localStorage.setItem(key, JSON.stringify(state))
})
}
createPinia().use(PersistPlugin)
社區現成方案:pinia-plugin-persistedstate,支持 sessionStorage / 加密 / 指定字段。
⚠️ 常見踩坑 & 提示
| 症狀 | 原因 / 解決 |
|---|---|
storeToRefs 解構後失響應 |
直接解構 const { count } = store 會丟響應;用 storeToRefs() |
| SSR Hydration 警告 | server & client store 不一致;確保 defineStore(id,<same initial state>) |
| 組件外修改 store 沒熱更新 | 需要開啓 HMR:if (import.meta.hot) import.meta.hot.accept(…) (Vite 模板已自動) |
| action 內部解構丟失 this | const { count } = this 丟響應;用 storeToRefs 或直接 this.count++ |
📝 一圖速記
defineStore() ──┬── state → 直接讀/改
│
├── getters → computed
│
└── actions → sync / async
│
└── 調用內部 this.count++ 或 this.$patch()
設計哲學:最少 API + 最大類型安全,讓“全局狀態”像寫 Composition API 一樣自然。
結語
- Pinia 並非“Vuex 改名”,而是 擁抱 Composition API、TypeScript、輕量化 的全新實現。
- 主流 Vue 3 項目(Nuxt 3、VitePress、UnoCSS 官網)都已默認 Pinia。
- 學會:設計理念 → store 定義 → 組件使用 → 插件持久化,進階 Vue 狀態管理就算畢業!
好啦,今天的內容就先到這裏!如果覺得我的分享對你有幫助,給我點個贊,順便評論吐個槽,當然不要忘了三連哦!感謝大家的支持,記得常回來,我是wangfang呀等着你們的下一次訪問!