Vue3项目Pinia封装实战:模块化+持久化,一次搞定全局状态管理
用vue3做项目,状态管理绕不开Pinia。作为官方推荐的状态管理库,Pinia比之前的Vuex好用太多了。今天带大家用模块化、工程化的方式重新封装Pinia,再配上持久化插件,解决实际开发中页面刷新状态丢失的痛点,给Vue3通用模板搭一个稳定、易用的全局状态管理。
为啥选Pinia
Pinia对比Vuex有三个明显优势,这也是我们选它的原因:
api更简洁:不用再写Vuex那一套繁琐的modules、mutations,写法直观,上手快
TS支持更好:原生适配TypeScript,类型推导准,开发时有语法提示,少报错
组合式风格更灵活:和Vue3的setup语法完美搭在一起,代码逻辑连贯,好复用
封装Pinia其实就两步:创建Pinia实例并配好持久化插件、把实例注册到Vue应用里。简单高效,直接开干。
一、装依赖
用create-vue脚手架建项目时,Pinia核心依赖已经默认装好了。我们只需要额外加个状态持久化插件,解决页面刷新后全局状态丢失的问题。
执行安装命令:
pnpm add pinia-plugin-persistedstate这个插件会自动把Pinia里的状态存到本地存储(默认用localStorage),不用自己手写localStorage或sessionStorage操作代码,开发省事不少。
二、优雅封装Pinia,实现模块化注册
为了让项目结构规范、后面好维护,我们在src/stores目录下统一封装Pinia,实现模块化注册,避免在main.ts里堆太多配置代码。
1. 写Pinia封装文件
在src/stores目录下建index.ts文件,实现Pinia实例创建、插件挂载和模块化注册方法:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import type { App } from 'vue'
// 创建Pinia核心实例
const pinia = createPinia()
// 挂载持久化插件,默认把状态存到localStorage,key用store唯一标识
pinia.use(piniaPluginPersistedstate)
/**
* 模块化注册Pinia
* @param app Vue应用实例
*/
export const installStore = (app: App) => {
app.use(pinia)
}
export default pinia2. 在Vue应用里注册Pinia
在项目入口文件main.ts里,调用封装好的installStore方法,把Pinia注册到Vue应用中。注册顺序建议放路由之后、应用挂载之前:
import { createApp } from 'vue'
import App from './App.vue'
import { installRouter } from '@/router'
import { installStore } from '@/stores'
const app = createApp(App)
// 先注册路由
installRouter(app)
// 再注册Pinia
installStore(app)
// 最后挂载应用
app.mount('#app')这种模块化注册方式让入口文件逻辑更清爽。后面要改Pinia配置,直接在src/stores/index.ts里调就行,符合单一职责原则。
三、实战测试Pinia,验证持久化效果
配完了得试试,用个计数器案例测一下Pinia核心功能和持久化效果。同时按业务模块化组织Store文件,贴合实际开发场景。
1. 模块化组织Store文件
在src/stores目录下建modules子目录,放不同业务模块的Store文件(比如用户模块、商品模块、系统模块)。这样状态管理结构清晰,团队协作和后面扩展都方便。
2. 创建测试Demo Store
在src/stores/modules里建demo.ts文件,用组合式API风格写测试Store,和Vue3的setup语法保持一致,这也是推荐写法:
import { defineStore } from 'pinia'
import { ref } from 'vue'
/**
* 测试用Pinia Store
* 组合式API风格,和Vue3 setup语法完美搭
*/
export const useDemoStore = defineStore(
'demoStore', // Store唯一标识,不能重复
() => {
// 定义状态:基础类型用ref,保持响应性
const counter = ref<number>(0)
// 定义Action:改状态的方法,统一管理状态变更
const increment = () => {
counter.value++
}
// 返回需要暴露的状态和方法
return { counter, increment }
},
{
persist: true, // 开启当前Store的状态持久化
}
)几个要点:
开了persist: true,这个Store的状态会自动存到localStorage,默认key用Store唯一标识demoStore
实际开发时可以自定义persist配置,比如指定用sessionStorage、只持久化某些字段,适配复杂场景
建议始终通过Action改状态,别直接动state,保证状态变更能追踪
3. 写Vue测试页面
建个demo.vue组件,调测试Store,实现计数器功能,验证Pinia的响应性和持久化效果:
<template>
<div class="demo">
<h2>Pinia 功能测试</h2>
<h3>计数器: {{ counter }}</h3>
<button @click="demoStore.increment()"> 点击+1 </button>
</div>
</template>
<script setup lang="ts">
import { useDemoStore } from '@/stores/modules/demo'
import { storeToRefs } from 'pinia'
// 初始化Store实例
const demoStore = useDemoStore()
// 用storeToRefs解构状态,保持响应性(直接解构会丢响应性)
const { counter } = storeToRefs(demoStore)
</script>关键点:解构Pinia里的状态时,必须用storeToRefs,不然Vue的响应式特性就丢了,这是开发中容易踩的坑。
4. 测试步骤与效果验证
启动项目,访问demo页面,按下面步骤测试,直观看看Pinia和持久化插件配得咋样:
页面刚加载,计数器默认是0,符合Store里的初始定义
点「点击+1」按钮,计数器每次+1,验证Pinia的响应性
刷新页面,计数器数值没变,验证持久化生效(状态存到localStorage了)
如果把demo.ts里的persist改成false,再刷新页面,计数器会变回0,进一步验证持久化配置的作用
测试通过,说明Pinia和持久化插件配好了,可以正常用。
四、开发小建议
Vue3项目里,既然组件层面已经用了组合式API(setup语法),那Store层面也建议用同样的风格,别用Vuex那种选项式写法。
好处是保持整个项目代码风格统一,团队协作时理解成本低。而且组合式API写法灵活,能把通用状态逻辑抽成可复用函数,开发效率更高。
五、本次封装总结
通过这次实操,给Vue3通用模板整了个Pinia优雅封装,核心亮点:
模块化封装:Pinia配置抽到src/stores目录,入口文件只调注册方法,代码清爽
集成持久化:用pinia-plugin-persistedstate,一行配置就解决了页面刷新状态丢失的问题
风格统一:用组合式API写Store,和Vue3生态完美搭
工程化组织:按业务模块分Store文件,后面维护和扩展都方便
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!