一、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)集成的项目