一、为什么要讲 Pinia
原因
Vue3 官方推荐的状态管理方案
用来替代 Vuex(Vuex 5 已停止推进)
更符合 Vue3 中 Composition API 的设计理念
比起 Vuex 使用更加方便
vuex 与 pinia 对比
| 特性 | Vuex (3/4) | Pinia |
|---|---|---|
| 核心组成 | State, Getters, Mutations, Actions | State, Getters, Actions |
| Mutations | 必须通过它修改 State (繁琐) | 废弃,Actions 即可修改 State |
| TypeScript | 支持较弱,需大量额外定义 | 原生完美支持,类型推断极佳 |
| 模块化 | 单一 Store 树,需嵌套 Modules | 多 Store 设计,扁平化结构,自动拆包 |
| 体积 | 约 10kb | 约 1kb (极度轻量) |
二、什么是状态管理(State Management)
什么是「状态」?
在前端应用中,状态(state)指的是:
状态 = 程序运行时”留在内存里、会变化的数据”
状态的核心特征
| 特征 | 说明 | 例子 |
|---|---|---|
| 存在于内存中 | 页面刷新即消失(除非持久化) | 用户登录后的 token |
| 会随时间变化 | 用户操作、API 响应会改变它 | 购物车商品数量增减 |
| 影响 UI 渲染 | 状态变 → 视图自动更新 | 切换主题色后界面变色 |
| 需要被追踪 | 框架通过响应式系统监控变化 | Vue 的 ref/reactive |
不使用状态管理会遇到什么问题?
当项目变大后,会出现:
父子组件层层传 props
兄弟组件通信复杂
数据分散在各个组件,难以维护
同一份数据被多处复制,容易不一致
为什么需要状态管理?
状态管理就是把这些状态从”散落在各个组件里”抽出来,集中存、集中改、集中通知,让任何组件都能同一份数据源、统一规则地读取和更新,避免:
父→子→孙,一层层
props传递事件一层层
emit回去不同组件各自 copy 一份数据,改完互相不一致
总结
状态 = 应用运行时需要被框架追踪、会影响视图、且可能被多个组件共享的”活”数据
(而状态管理 = 让这些”活”数据变得有序、可控、可预测的工具)
三、Pinia 在项目中的定位
1 | Vue App |
四、Pinia 的核心概念
Store 是什么
Store 是保存状态 ( state ) 和业务逻辑的实体,它并不与组件树绑定。
换句话说,它存储着全局的状态数据。
一个 Store 由哪几部分组成
| 部分 | 作用 |
|---|---|
| state | 定义状态数据 |
| getters | 类似计算属性(派生状态) |
| actions | 业务逻辑 / 异步操作 |
总结类比
可以把 pinia 类比为一个一个中心化的“仓库”。就像一个公司有一个中央档案室,不管哪个 部门(Vue 组件)需要 看 或 修改 某个 文件(数据),都得去这个档案室拿取。
解释:
各部门:各个 Vue 组件
文件:State 数据
看:组件调用 Store 里面 State 状态数据
修改:Store 里面修改 State 状态数据的方法(Actions)
都得去这个档案室拿取
- 我们如果想要在 Vue 组件里面使用 Store 仓库里面的数据与方法,肯定要先在组件里引入使用这个 Store 仓库
五、Pinia 的基础使用流程
安装 Pinia
1 | npm install pinia |
在 main.ts 中注册 Pinia
1 | import { createApp } from 'vue' |
定义一个 Store
1 | import { defineStore } from 'pinia' |
在组件中使用 Store
1 | // 1. 导入 |
六、Pinia 的两种写法
1. Options Store(类似 Vuex,适合新手)
1 | // 选项式写法 |
2. Setup Store(推荐写法)
1 | // 组合式写法(推荐) |
Setup Store 与 Composition API 思想完全一致
七、Pinia 中的数据更新原则
1. 可以直接修改 state
1 | // 可以在引入 Store 仓库的组件里面直接更改 |
为什么不推荐
虽然可以直改,但在中大型项目中,如果随处直接修改 Store 数据,会带来如下一些问题:
业务逻辑碎片化
如果修改数据的逻辑(比如:修改价格前要检查库存、计算折扣、验证权限)散落在 10 个不同的
.vue文件里。后果:当你需要修改这个逻辑时,你得满项目找这 10 个地方。如果写在
action里,你只需要改 Store 里的一个函数
调试与追踪困难
Pinia 的 Action 像是一个“带名字的事务所”
直接修改:在 DevTools 里,你可能只看到数据变了,但不知道是哪个组件、因为什么逻辑触发的
Action 修改:DevTools 会记录下 Action 的名称和参数,你可以清晰地看到“用户点击了支付按钮 -> 触发了
checkoutAction -> 状态变更”
2. 推荐在 actions 中集中修改
优点:
逻辑集中
方便调试
易于维护
八、Pinia 数据持久化
介绍:
Pinia 数据持久化是指将 Store 中的数据自动保存到浏览器的本地存储
localStoragesessionStorage
这样即使用户刷新页面或关闭浏览器后再次打开,数据依然能够恢复
对于记住用户的登录状态、购物车内容、主题偏好等场景非常有用
Pinia 官方并没有内置持久化插件,但社区提供了一个非常流行且好用的官方推荐插件
pinia-plugin-persistedstate
pinia-plugin-persistedstate的使用方法:- 安装插件:首先,我们需要在我们的项目里面安装
pinia-plugin-persistedstate插件
1
2
3
4
5npm install pinia-plugin-persistedstate
# 或者
yarn add pinia-plugin-persistedstate
# 或者
pnpm add pinia-plugin-persistedstate在 Pinia 实例中注册插件
1
2
3
4
5
6
7
8
9
10
11
12// main.js 或 store/index.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import persistedstate from 'pinia-plugin-persistedstate'; // 导入插件
const app = createApp(App);
const pinia = createPinia();
pinia.use(persistedstate); // 注册插件
app.use(pinia);
app.mount('#app');在 Store 定义中启用持久化
在使用
defineStore定义 Store 时,通过添加persist: true或一个配置对象来启用持久化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39// 示例1:简单的配置
// stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', () => {
// Store 配置项
// state、getter 和 action 都写这里面
, {
// 在这里添加持久化配置
persist: true // 最简单的形式,会持久化整个 Store 的 state
});
// ================================================================
// 示例 2:更详细的配置
export const useUserStore = defineStore('user', () => {
// Store 配置项
}, {
persist: {
// key: 自定义存储的键名,默认为 storeId
key: 'my_user_store_key',
// storage: 选择存储方式,默认为 localStorage
storage: localStorage, // 或 sessionStorage
// paths: 指定需要持久化的 state 字段(数组)
// 如果不设置,则默认持久化整个 state
paths: ['profile', 'preferences'], // 只持久化 profile 和 preferences
// 可选:自定义序列化函数 (serializer)
serializer: {
serialize: JSON.stringify,
deserialize: JSON.parse,
},
// 可选:自定义存储逻辑 (advanced)
// 如需要对存储的数据进行加密/解密等操作
}
});配置项详解:
persist: true:- 最简单的配置,会将整个 Store 的
state(即return出来的ref和reactive对象)完整地保存到localStorage中。
- 最简单的配置,会将整个 Store 的
persist: { ... }:key(string): 指定在浏览器存储中使用的键名。如果不指定,默认使用defineStore时的第一个参数(即storeId)。storage(Storage): 指定使用的 Web Storage 接口。可以是localStorage(数据永久保存,除非手动清除)或sessionStorage(数据仅在当前会话期间有效,关闭浏览器标签页/窗口后消失)。默认为localStorage。paths(Array<string>): 一个字符串数组,用于指定哪些state属性需要被持久化。如果设置了paths,则只有列出的属性会被保存,其他属性会被忽略。这对于不想持久化临时数据或敏感信息(如 token)非常有用。serializer(object): 允许你自定义数据的序列化和反序列化方式。默认使用JSON.stringify和JSON.parse。在某些特殊情况下可能需要自定义。
- 安装插件:首先,我们需要在我们的项目里面安装