Pinia 实战指南:Vue 3 的新一代状态管理工具
1. 什么是 Pinia?
Pinia 是 Vue 官方推荐的状态管理库,专为 Vue 3 构建,使用 TypeScript 编写,API 设计现代、简洁。Pinia 是 Vuex 的“精神继任者”,其设计理念更加贴近 Composition API 和模块化思维。
它主要用于:
- 在多个组件间共享状态
- 解耦复杂逻辑
- 替代 props 和 emit 的繁琐传参
- 管理全局数据(用户信息、权限、缓存配置等)
2. 适用场景:什么时候该用 Pinia?
Pinia 最适合用于中大型 Vue 3 项目,尤其是在以下场景中非常适用:
- 组件之间共享数据(非父子)
- 比如:用户登录信息、主题色、系统配置等
- 需要持久化状态或异步加载数据
- 比如:菜单列表、权限控制、缓存设置、token 等
- 状态变化需要在多个页面响应
- 比如:购物车、订单状态、通知数目等
- 避免 props 和 emit 层层传递造成的组件嵌套混乱
3. 安装与基本使用
3.1 安装 Pinia
npm install pinia
3.2 注册 Pinia
// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');
4. 📂 创建一个 store(🌰:用户信息)
Pinia 使用 defineStore 来定义一个 store,useStore 用于在组件中访问这个 store。
// stores/user.ts
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null as null | { id: string; name: string },
// 定义其他状态
}),
getters: {
isLoggedIn: (state) => !!state.token,
},
actions: {
setToken(token: string) {
this.token = token;
},
async fetchUserInfo() {
const res = await fetch('/api/user');
this.userInfo = await res.json();
},
logout() {
this.token = '';
this.userInfo = null;
},
// 定义其他方法
},
});
4.1 在组件中使用
<script setup lang="ts">
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
// 使用状态
console.log(userStore.token);
// 修改状态
userStore.setToken('new-token');
// 调用异步方法
await userStore.fetchUserInfo();
</script>
4.2 Composition 风格:setup 语法糖
也可以使用 defineStore() 的 setup 模式,结合 Composition API:
// stores/counter.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const double = computed(() => count.value * 2);
function increment() {
count.value++;
}
return { count, double, increment };
});
更适合小模块的组合与拆分。
5. 实用技巧
5.1 状态持久化(如登录信息)
import { defineStore } from 'pinia';
import { useStorage } from '@vueuse/core';
export const useUserStore = defineStore('user', () => {
const token = useStorage('token', '');
return { token };
});
🔐 结合 @vueuse/core 可轻松实现持久化,甚至支持 localStorage/sessionStorage 切换。
那什么是 @vueuse/core 呢?
一句话介绍:其是一个专为 Vue 3(支持 Composition API)设计的组合式函数库,提供了大量的高质量、响应式的工具函数,极大简化日常开发。本质是一套帮助我们写 Vue 逻辑更省心的「组合式函数工具箱」,有点像是 Vue 世界里的 “Lodash + Hooks + 工具函数合集”。
安装方式
npm install @vueuse/core
然后直接在 Vue 组件里使用,比如:
1、useLocalStorage - 响应式 localStorage
import { useLocalStorage } from '@vueuse/core'
const token = useLocalStorage('token', '')
token.value = 'abc123' // 自动同步到 localStorage
在 Pinia 里用它可以实现状态持久化,比手写 localStorage 更优雅。
2、useDark - 黑夜模式切换
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark() // 根据系统或浏览器默认模式
const toggleDark = useToggle(isDark)
toggleDark() // 调用切换深色/浅色
3、useDebounceFn - 函数防抖
import { useDebounceFn } from '@vueuse/core'
const search = useDebounceFn((value) => {
console.log('搜索:', value)
}, 300)
4、useEventListener - 监听任意事件
import { useEventListener } from '@vueuse/core'
useEventListener(window, 'resize', () => {
console.log('窗口大小改变')
})
注意事项:
- 必须 Vue 3 项目,Composition API 风格。
- useXxx() 函数 都是响应式封装,不要解构。
- 某些函数依赖 DOM API,需要运行在浏览器环境中(如 SSR 要注意)。
- 可配合 Pinia、Vant、Element Plus、Vue Router 等一同使用,提升开发效率和可维护性。
🆚 和 Lodash、手写工具函数对比
对比项 | Lodash | 手写工具 | @vueuse/core |
---|---|---|---|
响应式支持 | ❌ 无 | ❌ 无 | ✅ 有 |
Vue 生态集成 | ❌ | ❌ | ✅ |
封装程度 | 中等 | 看自己写得怎么样 | ✅ 极高 |
使用成本 | 简单 | 灵活但易出错 | 简单、安全、组合性强 |
5.2 解耦模块 & 动态加载 store
使用时再加载模块,避免初始时注册过多模块:
import { storeToRefs } from 'pinia';
const userStore = useUserStore();
const { token } = storeToRefs(userStore);
5.3 热更新支持(开发时体验超好)
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot));
}
6. 📈 优缺点
优势 | 描述 |
---|---|
更轻量 | 没有 mutation 的冗余设计,api 更简洁 |
类型推导好 | TypeScript 支持优秀,代码提示一流 |
支持组合式 | 与 Composition API 完美兼容 |
更易模块化 | Store 设计天然支持按需加载 |
响应式天然继承 | 状态就是 ref/reactive 对象 |
热更新支持 | HMR 默认支持,无需额外配置 |
状态透明 | 就是普通变量,更容易调试 |
缺点 | 描述 |
---|---|
Vue 2 不兼容 | 只能用于 Vue 3 项目 |
插件生态还不如 Vuex 丰富 | 一些高级场景还需手写或三方库 |
需要习惯新范式 | 对 Vuex 用户来说需要转变 mindset |
6.1 与 Vuex 的对比
维度 | Vuex | Pinia |
---|---|---|
API 风格 | 传统、模块多(state、getter、mutation、action) | 简洁(只有 state、getter、action) |
响应式支持 | Vue2 风格响应式 | Composition API 响应式 |
TypeScript | 支持但不自然 | 极佳 |
模块管理 | 需要注册 module | 文件即模块 |
状态定义 | state 是对象函数 | state 是 ref/reactive |
调用方式 | commit + dispatch | 直接方法调用 |
插件生态 | 丰富,官方插件多 | 逐渐完善中 |
6.2 使用注意点
1、Pinia 是响应式的,当状态发生变化时,相关的视图会自动更新。但不是自动深监听:使用 ref({}) 时要注意解构导致丢失响应式。
2、避免解构 state 内部变量,可以使用 storeToRefs:
const { token } = storeToRefs(useUserStore());
3、在非组件中使用 Store:直接调用 useXXXStore() 即可,前提是 pinia 已注册。
4、不要在 defineStore 外部使用 store 实例,否则可能影响响应式。