Skip to content

12-pinia缓存集成

pinia官网

概述

INFO

先看两种方式,就和vue3的组合式api一样

setup不能用$reset重置,要手动写,所以用option吧

1.Option Store

与 Vue 的选项式 API 类似,我们也可以传入一个带有 stateactionsgetters 属性的 Option 对象

  • state,就跟vue页面中那个data变量一样
  • getters,类似于computed计算属性
  • actions,和methods一致
js
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

2.Setup Store

也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

js
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})

我这里用的是setup语法,根据个人习惯而定。如果是为了维护,项目过大,那就用option。如果使用pinia较少的小项目,可以用setup方式


一、安装

shell
npm install pinia

二、配置全局引入

main.js

js
// pinia
import { createPinia } from 'pinia';
const pinia = createPinia();
app.use(pinia);
// store
import store from '@/store';
app.config.globalProperties.$store = store;

三、匹配子模块

src下创建store文件夹,然后创建index.js

并且新建modules文件夹

TIP

通过正则来获取子模块,通常都有子模块,最好别只用一个index.js来编写所有存储变量

js
// 拿到modules下的所有文件
const modulesFiles = import.meta.globEager('./modules/*.*');
const modules = {};
for (const key in modulesFiles) {
  const moduleName = key.replace(/(.*\/)*([^.]+).*/gi, '$2');
  const value = modulesFiles[key];
  modules[moduleName] = value;
  // console.log(modules);
}

export default modules;

解决报错

如果你是vite5的话,你会跟我一样报错

image-20240120155223792

TIP

因为这是vite4的写法,翻看官方文档

image-20240120155320786

v4迁移v5的特性 -->入口

这里说明了, import.meta.globEagerglobEager改成了glob

要加上eager属性为true,默认懒加载是false,关闭懒加载就是同步代码了. 所以要给true

多看文档

这里由于懒加载eager属性,为后面埋了个雷,请一步步看完,全局使用会有问题、

四、测试

1.简单测试(按需使用)

INFO

这里的简单测试,指的是按需引用,就是建了modules下的子模块,然后页面上单独引入这个子模块。

我知道,在大型项目开发时。store都是挂在全局的,随时都能用,减少引入的麻烦

例如:我这里贴个vuex的用法

./image-20240120163618062

test.js

去对应目录创建文件src/store/modules/test.js

js
import { defineStore } from 'pinia';

export const useTestStore = defineStore('test', () => {
  const count = ref(0);
  function countAdd() {
    count.value++;
  }
  return { count, countAdd };
});

App.vue

html
<template>
  <h1>{{ testObj.count }}</h1>
  <el-button type="primary" @click="onClick">点击</el-button>
</template>

<script setup>
import { useTestStore } from '@/store/modules/test';
const testObj = useTestStore();

function onClick() {
  return testObj.countAdd();
}
</script>

如果页面点击可以添加,说明调用成功了。


这里再进行改写,把testObj.count赋值到本页面中,

为什么不用直接赋值? let count = testObj.count

使用toRefs是为了不丢失响应性

html
<template>
  <h1>{{ testObj.count }}</h1>
  <h1>{{ count }}</h1>
  <el-button type="primary" @click="onClick">点击</el-button>
</template>

<script setup>
import { useTestStore } from '@/store/modules/test';
const testObj = useTestStore();
let { count } = toRefs(testObj); 
let { countAdd } = testObj; 

function onClick() {
  return testObj.countAdd(); 
  return countAdd();
}
</script>

其实可以省掉const testObj = useTestStore();

2.复杂测试(挂载全局)

main.js

js
import store from '@/store';
app.config.globalProperties.$store = store;

这里挂载到main.js,跟vue2的 Vue.prototype.$store= store道理一样

App.vue

很奇怪,我用的vite5,跟教程作者用vite4大相径庭。用法不一样

看区别

他的代码

<template>
  <h1>{{ count }}</h1>
  <button @click="handleClick">click</button>
  <br />
  <h1>{{ $store.test.useTestStore().count }}</h1>
  <button @click="$store.test.useTestStore().countAdd">click</button>
</template>

<script setup>
const { proxy } = getCurrentInstance();
let useTestStore = proxy.$store.test.useTestStore();
let { count } = toRefs(useTestStore); // 响应式
let { countAdd } = useTestStore;

function handleClick() {
  countAdd();
}
</script>

我会报错,就很奇怪

但是我把proxy.$store.test单独拿出来。

js
proxy.$store.test().then((res) => {
  let { count } = res.useTestStore();
  console.log(`count -->`, count);
});
// 写成这样又可以,我一直找不到哪里导致异步
./image-20240120171508412

我服啦,我差不多找了一个半小时原因。

原因就是vite5的glob是异步的,开启这个属性就可以了

./image-20240120174423240

因为原作者他是vite4,使用的是globEager,我早该看到的为啥会简化为glob,单独分出来懒加载为eager属性

./image-20240120174758047

所以修改src/store/index.js

js
const modulesFiles = import.meta.glob('./modules/*.*', { eager: true });

五、pinia持久化插件

插件官网

概述

本插件兼容 pinia^2.0.0,在使用之前请确保你已经 安装 Piniapinia-plugin-persistedstate 丰富的功能可以使 Pinia Store 的持久化更易配置:

  • vuex-persistedstate 相似的 API
  • 所有 Store 均可单独配置
  • 自定义 storage 和数据序列化
  • 恢复持久化数据前后的 hook
  • 每个 Store 具有丰富的配置
  • 兼容 Vue 2 和 3
  • 无任何外部依赖

1.安装

shell
pnpm i pinia-plugin-persistedstate
shell
npm i pinia-plugin-persistedstate
shell
yarn add pinia-plugin-persistedstate

main.js

js
// pinia持久化存储
import { createPersistedState } from 'pinia-plugin-persistedstate';
pinia.use(
  createPersistedState({
    auto: true, // 启用所有 Store 默认持久化
  }),
);
./image-20240120210036940

INFO

pinia持久化的无法通过 window.localStorage.clear(); 一键清空数据

经测试我可以清空

如果不能通过那个语句清空

请看原文

2.重置state

TIP

同样的,重置的时候也有区别,option和setup两种不一样

  • 使用option 选项式 API 时,你可以通过调用 store 的 $reset() 方法将 state 重置为初始值。
js
const store = useStore()

store.$reset()
  • Setup Stores 中,您需要创建自己的 $reset() 方法:

案例

js
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  function $reset() {
    count.value = 0
  }

  return { count, $reset }
})

我是setup方式,然后我直接用this.$reset(),会报错

code-snapshotimage-20240120212955257

解决:

  1. 改用选项式api
  2. 重写 $reset 方法

总结

我翻看了若依vite+vue3的项目源码,发现若依大佬是option的写法

所以,pinia还是用option吧,这篇学会简单用法即可

若依vue3后台管理系统源码

若依项目扩展

image-20240120213346582

Released under the MIT License.