
Pinia 是 Vue 官方推荐的新一代状态管理库,旨在替代 Vuex,为 Vue 应用程序提供一种更直观、更轻量且对 TypeScript 支持极佳的状态共享方案。
一、Pinia 的核心优势
• 更简单的 API :Pinia 最大的改变之一是移除了 Vuex 中繁琐的 mutations。现在可以在 actions 中直接同步或异步地修改 state,大大简化了代码逻辑。
• 极佳的 TypeScript 支持:Pinia 在设计时就充分考虑了 TypeScript,提供了完整的类型推断,无需编写复杂的类型包装器,让开发者享受自动补全和类型安全带来的高效体验。
• 直观且模块化:它摒弃了 Vuex 中嵌套式的模块结构,采用扁平化的 Store 设计。每个 Store 都是独立且自动分离的,使得代码结构更清晰,更易于维护。
• 强大的开发工具支持:Pinia 与 Vue DevTools 紧密集成,提供时间线追踪、Store 状态预览等强大功能,让调试变得轻而易举。
• 支持热模块替换:在开发过程中,你可以修改 Store 并保留当前状态,无需重新加载页面,极大地提升了开发体验。
二、Pinia 的核心概念
Pinia 的核心概念围绕三个主要构建块:State 、Getters 和 Actions。
| 核心概念 | 描述 | 与 Vuex 对比 |
|---|---|---|
| State (状态) | 这是 Store 中存储数据的核心,是一个返回初始状态的函数。 | 与 Vuex 类似,但 Pinia 的 State 是函数形式,更利于服务端渲染。 |
| Getters (计算属性) | 用于定义基于 State 的派生数据,带有缓存功能,类似于 Vue 组件中的计算属性。 | 与 Vuex 的 Getters 概念和用途基本一致。 |
| Actions (动作) | 用于定义修改 State 或处理任意复杂业务逻辑的方法,尤其适合处理异步操作(如 API 调用)。 | 核心差异:Pinia 的 Actions 替代了 Vuex 中的 Mutations 和 Actions,可以直接修改 State,流程更简洁。 |
三、如何在 Vue 3 项目中使用 Pinia
1. 安装与配置
首先,在你的 Vue 3 项目中安装 Pinia:
bash
npm install pinia
# 或
yarn add pinia
然后,在 main.js 中创建 Pinia 实例并将其挂载到 Vue 应用上:
javascript
// 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')
对于使用 uni-app 的项目,HBuilder X 创建的 Vue 3 项目已内置 Pinia,可直接使用。
2. 创建一个 Store
在 src/stores 目录下创建一个 Store 文件(例如 counter.js)。使用 defineStore 函数定义一个 Store,它接收一个唯一的 id 作为第一个参数。
两种语法风格:
• 选项式 API (Options Store) :与 Vuex 风格类似,通过 state、getters、actions 属性定义。
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Pinia'
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
async registerUser(login, password) {
// 可以在此处理异步逻辑
// this.userData = await api.post(...)
}
},
})
• 组合式 API (Setup Store) :与 Vue 3 的组合式 API 风格一致,使用 ref、computed 和函数来定义。
javascript
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const name = ref('Pinia')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, name, doubleCount, increment }
})
3. 在组件中使用 Store
在组件中,直接导入并使用你定义的 Store 函数。
javascript
<script setup>
import { useCounterStore } from '@/stores/counter'
// 获取 store 实例
const counterStore = useCounterStore()
// 直接修改 state (方式1: 最简单)
const handleDirectIncrement = () => {
counterStore.count++
}
// 使用 $patch 方法批量修改 (方式2: 适合同时修改多个值)
const handlePatchUpdate = () => {
counterStore.$patch({
count: counterStore.count + 2,
name: 'Updated by $patch'
})
}
// 调用 action (方式3: 适合封装逻辑)
const handleActionIncrement = () => {
counterStore.increment()
}
</script>
<template>
<p>Count: {{ counterStore.count }}</p>
<p>Double Count: {{ counterStore.doubleCount }}</p>
<button @click="handleDirectIncrement">直接 +1</button>
<button @click="handlePatchUpdate">批量更新</button>
<button @click="handleActionIncrement">通过 Action +1</button>
</template>
注意 :为了保持响应式,从 Store 中解构属性时,应使用
storeToRefs工具函数。例如:
javascriptimport { storeToRefs } from 'pinia' const { count, name } = storeToRefs(counterStore)
四、Pinia vs Vuex:主要差异对比
| 特性 | Pinia | Vuex 3.x / 4.x |
|---|---|---|
| Mutations | 无。状态变更直接在 Actions 中进行。 | 有。必须通过 Mutations 进行同步的状态修改。 |
| TypeScript 支持 | 原生、卓越的类型推断,无需额外配置。 | 支持但需要编写较多类型定义,体验相对复杂。 |
| 模块化 | 天生模块化,每个 Store 都是一个独立模块,无需嵌套。 | 需要使用 modules 进行嵌套式模块注册。 |
| 代码体积与复杂度 | 更轻量,API 简洁明了。 | 相对更重,模板代码较多(如常量 mutation 类型)。 |
| 适用版本 | 主要面向 Vue 3,也有限支持 Vue 2。 | Vuex 3 用于 Vue 2,Vuex 4 用于 Vue 3。 |
五、最佳实践与进阶技巧
• 组织 Store 文件 :通常建议在项目根目录下创建 stores 文件夹,并按业务模块划分文件,如 stores/user.js、stores/cart.js 等,保持清晰的项目结构。
• 在 Actions 中访问其他 Store:Pinia 支持 Store 之间的互相调用。只需在 Action 中导入并使用其他 Store 即可,这使得跨模块状态交互变得非常自然。
• 状态重置 :Pinia 提供了 $reset() 方法,可以方便地将 Store 的状态重置为其初始值。
• 插件化 :Pinia 支持通过插件扩展功能,例如实现状态的持久化存储(结合 pinia-plugin-persistedstate 等插件)。
总而言之,Pinia 凭借其简洁的设计、完整的 TypeScript 支持和与现代 Vue 3 开发范式的高度契合,已成为管理 Vue 应用程序状态的理想选择。