Pinia 实战速成:Vue3状态管理利器

103 阅读4分钟

之前学习过vue的官方状态管理Vuex,当时是基于Vue2的学习,现在已经Vue3了,那么继续学习吧。

当时Vuex链接:快速学一遍vue的状态管理模式 -- Vuex_vue的action,view,state,store-CSDN博客

理论上Vue3也可以继续用Vuex,不过Vue 官方现在推荐使用 Pinia 作为 Vue3 的默认状态管理库。Pinia 由 Vue 核心团队维护,提供了更简洁的 API 和更好的 TypeScript 支持。

为什么选择Pinia ?

  1. 更简单的 API:Pinia 移除了 Vuex 中的 mutations 概念,简化了状态更新流程
  2. TypeScript 支持:Pinia 从设计之初就考虑了 TypeScript,提供完整的类型推断
  3. 组合式 API 友好:与 Vue3 的组合式 API 风格高度契合
  4. 模块化设计:不再需要嵌套模块,每个 store 都是独立的
  5. 更轻量:Pinia 的体积比 Vuex 小很多

核心概念对比

概念VuexPinia
状态statestate
获取状态gettersgetters
修改状态mutations直接修改或 actions
异步操作actionsactions
模块系统modules独立的 stores
TypeScript需要额外配置开箱即用

聊天室项目实践

在聊天室项目当中,用户登陆后返回的id和name值在其他页面上也经常需要被用到,一直访问后台获取增加了网络开销,造成过度查询,因为这两个数据并不私密,可以考虑客户端存储。

考虑到兼顾安全性和 便捷性,采用pinia和加密localStorage的方式。

主流存储方案对比

存储位置特点安全性持久性适用场景
Pinia/Vuex内存存储,响应式❌ 刷新丢失临时非敏感数据,组件间共享
LocalStorage持久存储,5MB上限⚠️ 明文存储永久需要长期保存的非敏感数据
SessionStorage标签页级存储⚠️ 明文存储标签页关闭失效单次会话的临时数据
Cookie可设置HttpOnly✅ 最安全可配置过期时间敏感数据(需配合后端)
VueUse useStorage响应式封装⚠️ 依赖底层存储可配置需要响应式的持久化数据

安装

piniacrypto进行安装,安装命令:

  • yarn add pinia
  • yarn add crypto-js

pinia使用

修改main.js文件以初始化pinia

  • 添加了Pinia的导入语句 import { createPinia } from 'pinia'
  • 创建了Pinia实例 const pinia = createPinia()
  • Pinia挂载到Vue应用 app.use(pinia)
import { createPinia } from 'pinia';

const pinia = createPinia();
...
app.use(pinia);

在全局挂载Pinia后,创建一个stores文件夹,用来存储保持状态和业务逻辑的实体:

  1. src文件夹下创建stores文件夹,然后再创建一个user.js文件,用来存储用户的信息
  2. 因为需要用到localstorage,这里的数据能在控制台看到,为了安全性考虑,使用crypto进行加密,在utils文件夹下创建一个crypto.js文件:
import CryptoJS from 'crypto-js';
const CRYPTO_KEY = import.meta.env.VITE_CRYPTO_KEY || 'kong-secure-key';

export const encrypt = (data) => {
    if(!CRYPTO_KEY) {
        throw new Error('加密密钥未配置');
    }
    return CryptoJS.AES.encrypt(data, CRYPTO_KEY).toString();
};

export const decrypt = (ciphertext) => {
    if(!CRYPTO_KEY) {
        throw new Error('加密密钥未配置');
    }
    return CryptoJS.AES.decrypt(ciphertext, CRYPTO_KEY).toString(CryptoJS.enc.Utf8);
};
  1. 最后采用Pinia + 加密LocalStorage 进行 🛡️安全存储,对于user的store进行定义,配置初始化设置用户信息,已经登出后重置状态清除本地存储用户信息,$resetpinia提供的内置方法。
import { defineStore } from 'pinia';
import { decrypt, encrypt } from '@/utils';

export const useUserStore = defineStore('user', {
    state: () => ({
        userId: null,
        userName: null,
    }),
    actions: {
        setUser(user) {
            this.userId = user.id;
            this.userName = user.name;
            // 加密后存LocalStorage
            localStorage.setItem('chat-user', encrypt(JSON.stringify(user)));
        },
        logout() {
            // 重置Pinia store的状态到初始值
            this.$reset();
            // 清除本地存储的用户信息
            localStorage.removeItem('chat-user');
        },
        initialize() {
            // 页面刷新时从LocalStorage恢复
            const saved = localStorage.getItem('chat-user');
            if (saved) {
                try {
                    const data = JSON.parse(decrypt(saved));
                    this.userId = data.id;
                    this.userName = data.name;
                } catch (e) {
                    this.logout();
                }
            }
        }
    },
})
  1. 经过上面的步骤,已经可以在其他页面中进行使用了:

    • 导入userUserStore对象:import { useUserStore } from '@/stores/user';
    • 创建一个实例:const userStore = useUserStore();
    • 获取userId:userStore.userId
  2. 这里页面刷新后,userStore.userId就获取不到了,所以需要调用上面定义时写的initialize方法,可以在每个页面的初始化处调用一次,我选择在路由导航守卫中调用,这样在每个vue页面初始化时,不用再额外调用initialize方法了:

router.beforeEach((to, from, next) => {
  // 需要排除的路由名称列表
  const excludeRoutes = ['login', 'register'];
  if (!excludeRoutes.includes(to.name)) { 
    const userStore = useUserStore();
    userStore.initialize();
  }
  next();
});
  1. 现在已经进行了登录,在本地中也已经存储了信息,获取store中userId也能获取到

pinia1.png

pinia2.png

对于常用的userId和username属性就已经实现了状态管理控制,如果需要管理一些其他变量,也可以在stores文件夹中创建新的store,这就是类似于仓库一样。


其实pinia的使用学习比较简单,上手速度极快,推荐在项目中直接使用

让我们边做边学吧, Let's Go!

QQ20250527-170207.jpg