Pinia 是 Vue.js 官方推荐的下一代状态管理库,它以其简洁的 API 设计和出色的 TypeScript 支持而闻名。作为 Vuex 的轻量级替代品,Pinia 提供了更直观、更现代化的状态管理解决方案。
Pinia 的核心优势
- 极简 API :移除了 Vuex 中复杂的
mutations,所有状态修改都可以在actions中完成(也支持直接修改),大大降低了学习成本和代码量。 - 完美的 TypeScript 支持:Pinia 的 API 设计从一开始就考虑了类型推断,无需复杂的包装器即可享受完整的 TypeScript 体验和自动补全。
- 模块化设计:没有嵌套模块的概念,每个 Store 都是独立且自组织的,可以通过导入方式自然组合,结构更清晰。
- 轻量高效:核心包体积非常小(约 1KB gzip),同时与 Vue DevTools 深度集成,提供出色的开发调试体验。
环境搭建
首先,在你的 Vue 3 + TypeScript 项目中安装 Pinia:
bash
npm install pinia
然后,在项目的入口文件(通常是 main.ts)中进行配置:
typescript
// main.ts
import { createApp } from 'vue' // 导入 createApp 函数用于创建 Vue 应用实例
import { createPinia } from 'pinia' // 导入 createPinia 函数用于创建 Pinia 实例
import App from './App.vue' // 导入根组件
const app = createApp(App) // 创建 Vue 应用实例
const pinia = createPinia() // 创建 Pinia 实例
app.use(pinia) // 将 Pinia 以插件的形式安装到 Vue 应用中
app.mount('#app') // 将 Vue 应用挂载到 HTML 元素 #app 上
核心概念实战:计数器 Demo
下面我们通过一个简单的计数器案例,演示如何使用 Pinia 进行状态管理。
1. 创建 Store (定义状态容器)
创建一个名为 counter.ts 的文件来管理计数器的状态逻辑:
typescript
// stores/counter.ts
import { defineStore } from 'pinia' // 导入 defineStore 函数,它是定义 Store 的核心 API
// 使用 defineStore 定义并导出一个名为 useCounterStore 的 Store
// 第一个参数 'counter' 是 Store 的唯一标识符(ID),用于调试和 DevTools
// 第二个参数是一个配置对象,用于定义 state, getters, actions
export const useCounterStore = defineStore('counter', {
// state 是一个函数,返回状态的初始值
// 这里使用 TypeScript 的箭头函数定义返回值的类型,确保类型安全
state: () => {
return {
count: 0 as number // 定义一个响应式状态 count,初始值为 0,类型为 number
}
},
// getters 类似于组件的计算属性 (computed),用于根据 state 派生数据
// 它们具有缓存特性,只有当依赖的状态发生变化时才会重新计算
getters: {
// 定义一个 getter,用于计算 count 的双倍值
// 参数 state 是当前 Store 的 state,TypeScript 会自动推断其类型
doubleCount: (state) => {
return state.count * 2 // 返回 count 值的两倍
}
},
// actions 用于封装修改状态的业务逻辑,可以是同步或异步的
// 在 actions 内部,可以通过 `this` 访问整个 Store 实例
actions: {
// 定义一个 increment action,用于增加 count 的值
increment() {
this.count++ // 通过 this 直接修改 state 中的 count
},
// 定义一个 decrement action,用于减少 count 的值
decrement() {
this.count-- // 通过 this 直接修改 state 中的 count
},
// 定义一个 incrementBy action,可以接收一个参数进行增量增加
// amount 参数类型为 number,表示增加的数值
incrementBy(amount: number) {
this.count += amount // 将 count 增加指定的 amount
}
}
})
2. 在组件中使用 Store
在一个 Vue 组件(例如 CounterComponent.vue)中,我们可以这样使用上面定义的 Store:
html
<!-- CounterComponent.vue -->
<template>
<!-- 模板部分 -->
<div class="counter">
<h2>Pinia 计数器示例</h2>
<!-- 直接从 Store 实例中读取 state -->
<p>当前计数: {{ counterStore.count }}</p>
<!-- 调用 Store 中定义的 getter -->
<p>双倍计数: {{ counterStore.doubleCount }}</p>
<div class="buttons">
<!-- 调用 Store 中的 actions 来修改状态 -->
<button @click="counterStore.increment()">+1</button>
<button @click="counterStore.decrement()">-1</button>
<button @click="counterStore.incrementBy(5)">+5</button>
<!-- 使用 $patch 方法进行批量更新,适合同时修改多个状态 -->
<button @click="handleReset">重置为 10</button>
</div>
</div>
</template>
<script setup lang="ts">
// 使用 <script setup> 语法,这是 Vue 3 的组合式 API 的语法糖
// 导入我们定义的 useCounterStore Hook
import { useCounterStore } from '@/stores/counter'
// 调用 useCounterStore 函数来获取 counterStore 实例
// 这个调用必须在 setup 函数或 <script setup> 顶层进行
const counterStore = useCounterStore()
// 定义一个方法,演示使用 $patch 方法修改状态
// $patch 可以接受一个对象,用于批量更新多个状态(本例中只有一个 count)
const handleReset = () => {
counterStore.$patch({
count: 10 // 将 count 状态重置为 10
})
}
</script>
<style scoped>
/* 组件的样式 */
.counter {
text-align: center;
margin: 20px;
}
.buttons button {
margin: 0 5px;
padding: 5px 10px;
}
</style>
使用 storeToRefs 保持响应式
当需要从 Store 中解构出状态或 getters 在组件中使用时,为了不丢失其响应性,可以使用 Pinia 提供的 storeToRefs 工具函数。
typescript
// 在组件 script setup 中的使用示例
import { storeToRefs } from 'pinia' // 导入 storeToRefs
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// ❌ 错误做法:直接解构会使数据失去响应性
// const { count, doubleCount } = counterStore
// ✅ 正确做法:使用 storeToRefs 解构可以保持响应性
// 它为每个响应式属性(state 和 getters)创建 ref 引用
const { count, doubleCount } = storeToRefs(counterStore)
// 注意:Actions 不需要也不应该通过 storeToRefs 解构,可以直接从原 store 实例上调用
// const { increment, decrement } = counterStore
总结
通过这个简单的计数器 Demo,我们可以看到 Pinia 的核心用法:
- 定义 Store :使用
defineStore函数,包含state,getters,actions。 - 使用 Store :在组件中调用
useXxxStore()函数获取 Store 实例,然后通过实例访问状态、调用方法。 - 修改状态 :可以直接修改 (
store.count++)、使用store.$patch方法、或调用封装好的actions。 - 保持响应式 :解构状态或 getters 时使用
storeToRefs。
Pinia 的 API 设计非常直观,与 Vue 3 的 Composition API 理念高度一致,大大简化了状态管理的复杂度。对于新项目,Pinia 无疑是 Vue 应用状态管理的首选方案。