在 Vue2 时代,Vuex 几乎是 Vue 项目状态管理的唯一标准答案。但随着 Vue3 的发布和 Composition API 的普及,Pinia 应运而生 ------ 它不仅是 Vuex 的官方替代品,更是为现代 Vue 开发量身打造的状态管理库。本文将带你从零开始认识 Pinia,掌握其核心用法,并理解它为何能成为 Vue3 生态的主流选择。
一、Pinia 是什么?
Pinia(发音 /piːnjə/)是 Vue 官方推荐的状态管理库,由 Vue 核心团队成员开发维护,其设计理念完全适配 Vue3 的 Composition API,同时也兼容 Vue2。相比 Vuex,Pinia 去掉了 Mutations、Modules 嵌套等繁琐设计,API 更简洁、类型支持更完善,是目前 Vue 项目状态管理的首选方案。
核心优势
- 极简 API:去掉 Vuex 的 Mutations,直接通过 Actions 修改状态,代码更简洁;
- 完美的 TypeScript 支持:天生适配 TS,无需额外配置即可获得完整的类型提示;
- 无嵌套模块:通过扁平化的 Store 设计替代 Modules,结构更清晰;
- 轻量高效:体积仅 1KB 左右,性能几乎无损耗;
- 兼容 Vue2/Vue3:一套代码可在两个版本的 Vue 中运行;
- 支持 DevTools:保留 Vuex 的调试体验,支持时间旅行、状态快照等功能。
二、快速上手:Pinia 基础使用
1. 安装 Pinia
首先在 Vue 项目中安装 Pinia(推荐使用 npm/yarn/pnpm):
javascript
# npm
npm install pinia
# yarn
yarn add pinia
# pnpm
pnpm add pinia
2. 注册 Pinia 到 Vue 应用
在项目入口文件(如main.js/main.ts)中引入并注册 Pinia:
javascript
// main.ts (Vue3 + TS)
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
// 创建Pinia实例并挂载到Vue应用
app.use(createPinia())
app.mount('#app')
3. 创建第一个 Store
Store 是 Pinia 的核心概念,用于存放全局状态和修改状态的方法。在src/stores目录下创建counter.js(推荐按功能划分 Store):
javascript
// src/stores/counter.js
import { defineStore } from 'pinia'
// 第一个参数是Store的唯一ID(必须唯一),第二个参数是配置对象
export const useCounterStore = defineStore('counter', {
// state:存储状态的函数,返回初始状态(必须是函数,避免状态共享)
state: () => ({
count: 0,
title: 'Pinia计数器'
}),
// getters:计算属性,基于state派生新值,类似Vue的computed
getters: {
// 计算双倍数值
doubleCount: (state) => state.count * 2,
// 访问当前Store的其他getter
doubleCountPlusOne(): number {
return this.doubleCount + 1
}
},
// actions:修改状态的方法,支持同步/异步,类似Vue的methods
actions: {
increment() {
this.count++ // 直接修改state
},
incrementBy(num: number) {
this.count += num
},
// 异步Action示例
async incrementAsync(delay: number) {
await new Promise(resolve => setTimeout(resolve, delay))
this.increment()
}
}
})
4. 在组件中使用 Store
在 Vue 组件中引入并使用创建好的 Store:
javascript
<!-- src/components/Counter.vue -->
<template>
<div class="counter">
<h2>{{ counterStore.title }}</h2>
<p>当前计数:{{ counterStore.count }}</p>
<p>双倍计数:{{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">+1</button>
<button @click="counterStore.incrementBy(5)">+5</button>
<button @click="counterStore.incrementAsync(1000)">1秒后+1</button>
</div>
</template>
<script setup lang="ts">
// 引入创建的Store
import { useCounterStore } from '@/stores/counter'
// 创建Store实例(Pinia会自动缓存,多次调用返回同一个实例)
const counterStore = useCounterStore()
</script>
三、Pinia 核心特性详解
1. 状态修改的三种方式
(1)直接修改(简单场景)
javascript
// 组件中直接修改state
counterStore.count = 10
(2)通过 Actions 修改(推荐,统一管理)
如上文示例中的increment、incrementBy方法,适合复杂逻辑或需要复用的修改操作。
(3)$patch 批量修改(适合多状态批量更新)
javascript
// 方式1:对象形式
counterStore.$patch({
count: counterStore.count + 10,
title: '更新后的标题'
})
// 方式2:函数形式(支持复杂逻辑)
counterStore.$patch((state) => {
state.count += 10
state.title = '更新后的标题'
})
2. Getters 的高级用法
- Getters 默认缓存:只有依赖的 state 变化时才会重新计算;
- 支持传参:通过返回函数实现(注意:传参后缓存失效);
javascript
// 带参数的getter
getters: {
getCountByNum: (state) => (num: number) => state.count + num
}
// 组件中使用
counterStore.getCountByNum(5) // 返回count+5
- 跨 Store 访问:在 getter/actions 中引入其他 Store 即可;
javascript
// 引入其他Store
import { useUserStore } from './user'
getters: {
userAndCount(state) {
const userStore = useUserStore()
return `${userStore.name}的计数:${state.count}`
}
}
3. 解构 Store 保持响应式
直接解构 Store 会丢失响应式,需使用storeToRefs:
javascript
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// 正确解构(保持响应式)
const { count, doubleCount } = storeToRefs(counterStore)
// actions无需解构,直接使用
const { increment } = counterStore
4. 重置状态
通过$reset方法恢复 Store 到初始状态:
javascript
counterStore.$reset()
5. 订阅 Store 变化
监听 Store 的状态修改:
javascript
// 订阅state变化
const unsubscribe = counterStore.$subscribe((mutation, state) => {
// mutation:包含修改的类型、路径等信息
// state:修改后的状态
console.log('状态变化了', mutation, state)
// 可用于持久化存储(如localStorage)
localStorage.setItem('counter', JSON.stringify(state))
})
// 取消订阅
// unsubscribe()
四、Pinia vs Vuex:核心差异
| 特性 | Pinia | Vuex 3/4 |
|---|---|---|
| Mutations | 移除,直接在 Actions 修改状态 | 必须通过 Mutations 修改状态 |
| Modules | 扁平化设计,每个 Store 独立 | 嵌套 Modules,需命名空间管理 |
| TypeScript 支持 | 天生支持,无需额外配置 | 需要复杂的类型声明 |
| 代码体积 | ~1KB | ~10KB |
| API 简洁度 | 极简,接近原生 Vue 语法 | 相对繁琐,需记忆更多概念 |
| 多 Store 交互 | 直接引入即可,无需根 Store | 需通过 rootState/rootGetters |
五、Pinia 最佳实践
-
按功能划分 Store :如
userStore(用户相关)、cartStore(购物车相关),避免单一 Store 过大; -
状态持久化 :结合
pinia-plugin-persistedstate实现本地存储(如 localStorage);javascript# 安装插件 npm install pinia-plugin-persistedstatejavascript// main.ts中注册插件 import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate)javascript// Store中启用持久化 export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), persist: true // 启用持久化 }) -
避免过度使用全局状态 :仅将跨组件共享的状态放入 Store,组件内部状态优先用
ref/reactive; -
Actions 中统一处理业务逻辑:将状态修改、接口请求等逻辑封装在 Actions 中,提高复用性;
-
结合组合式 API :在 Store 中可使用
ref、computed等 Composition API,逻辑更灵活。
六、总结
Pinia 凭借简洁的 API、完美的 TypeScript 支持和适配 Vue3 的设计理念,成为了 Vue 生态状态管理的最优解。它去掉了 Vuex 的冗余设计,保留了核心的状态管理能力,同时降低了学习和使用成本。无论是小型项目还是大型应用,Pinia 都能提供高效、清晰的状态管理方案。
从 Vuex 迁移到 Pinia 成本极低,如果你正在开发 Vue3 项目,不妨尝试 Pinia------ 它会让你的状态管理代码更简洁、更易维护。
关键点回顾
- Pinia 核心优势:极简 API、完美 TS 支持、无嵌套模块、轻量高效,是 Vue 官方推荐的状态管理库;
- 核心用法 :通过
defineStore创建 Store(包含 state、getters、actions),组件中通过useXxxStore使用,修改状态推荐用 Actions 或$patch; - 最佳实践:按功能拆分 Store、结合插件实现状态持久化、避免过度使用全局状态,Actions 中统一封装业务逻辑。