编辑
2023-04-03
Pinia
0
请注意,本文编写于 523 天前,最后修改于 513 天前,其中某些信息可能已经过时。

目录

State
说明
创建State初始值
State的持久化存储
两种方法
插件实现
安装插件
插件方法
SetupStore
开启数据缓存
高级技巧
配置
SetupStore中使用
默认情况
OptionsStore中使用
默认情况
修改State
直接修改
批量修改
说明
替换State
重置State
OptionsStore
说明
SetupStore
说明
解决方案
订阅State变化
说明
特点
EG

State

说明

vuex种的state类似,都是保存store的状态

创建State初始值

ts种会自动推导其类型

ts
import { defineStore } from 'pinia'; export default defineStore( 'userinfoStore', () => { const username = ref(''); const rolesname = ref(''); const token = ref(''); return { username, rolesname, token }; }, );

State的持久化存储

两种方法

  1. 手动设置本地存储localStorage
  2. 通过插件pinia-plugin-persist实现

插件实现

安装插件

pnpm i pinia-plugin-persist

插件方法

SetupStore

开启数据缓存
ts
import { defineStore } from 'pinia'; export default defineStore( 'userinfoStore', () => { const username = ref(''); const rolesname = ref(''); const token = ref(''); return { username, rolesname, token }; }, { persist: { enabled: true, // 开启数据缓存 } } );
高级技巧

strategies存储策略

ts
interface PersistStrategy { key?: string; // 存储的key 默认是 store.$id //存储方式 localStorage、sessionStorage (默认: sessionStorage) storage?: Storage; // 需要持久化的字段名 paths?: string[]; }
ts
import { defineStore } from 'pinia'; export default defineStore( 'userinfoStore', () => { const username = ref(''); const rolesname = ref(''); const token = ref(''); return { username, rolesname, token }; }, { persist: { enabled: true, // 开启数据缓存 strategies: [ { // 存储的key key: 'userinfo', // 存储方式 localStorage、sessionStorage storage: sessionStorage, // 需要持久化的字段名 paths: ['username', 'rolesname', 'token'], }, ], } } );

自定义策略

Storage的类型

ts
/* * 此Web API接口 提供对sessionStorage和localStorage的访问,允许添加、修改或删除存储的数据项 */ interface Storage { /* 返回键值对的数量 */ readonly length: number; /* * 如果存在存储的数据就删除全部的 */ clear(): void; /* * 从sessionStorage或localStorage中通过key取值,如果存在则返回,没有则返回 null */ getItem(key: string): string | null; /** 返回第n个键的名称,如果n大于或等于存储中键/值对的数量,则返回null. */ key(index: number): string | null; /* * 对于存在的键值对,就通过key删除 */ removeItem(key: string): void; /** * 设置由key/value标识的键/值对的值,如果之前key不存在,则创建一个新的键/值对 * 如果无法设置新值,则抛出“QuotaExceededError”DOMException异常。(设置可能会失败,例如,用户已经禁用了站点的存储,或者如果配额已经超过。) * key: store.$id,即创建strore时设置的id * value: 要设置的值,字符串类型 */ setItem(key: string, value: string): void; [name: string]: any; }
ts
import { defineStore } from 'pinia'; const cookiesStorage: Storage = { setItem(key, state) { console.log('setItem key state', key, state, typeof state) /* setItem key state userinfoStore {"rolesname":"EveryOne","token":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjgxMzA5ODMwLCJleHAiOjE2ODEA2MzB9.ySJZ3CBguR7I9HJJdtCqKri8cRT5EHt3jTpd8jYw"} string */ return sessionStorage.setItem(key, state); }, getItem(key) { console.log('getItem key ', key) return JSON.stringify({ tokens: sessionStorage.getItem(key), }); }, length: 0, clear: function (): void { console.log('clear') }, key: function (index: number): string | null { console.log('index', index) return index.toString() }, removeItem: function (key: string): void { console.log('key', key) } } export default defineStore( 'userinfoStore', () => { const username = ref(''); const rolesname = ref(''); const token = ref(''); return { username, rolesname, token }; }, { persist: { enabled: true, // 开启数据缓存 strategies: [ { // 存储的key key: 'userinfo', // 存储方式 localStorage、sessionStorage storage: sessionStorage, // 需要持久化的字段名 paths: ['username', 'rolesname'], }, { // 存储方式为自定义的cookiesStorage storage: cookiesStorage, // 需要持久化的字段名 paths: ['token'], }, ], } } );

配置

store目录下新建index.ts文件

ts
import { createPinia } from 'pinia'; // 数据持久化插件 import piniaPluginPersist from 'pinia-plugin-persist'; const store = createPinia(); // 注册插件 store.use(piniaPluginPersist); export default store;

main.ts中配置

import { createApp } from 'vue'; // 引入全局样式文件 import './assets/css/global.less'; import App from './App.vue'; import router from './router'; import store from './pinia'; createApp(App).use(store).use(router).mount('#app');

SetupStore中使用

默认情况

  • LoginAndRegister.ts中使用

默认是sessionStorage存储,键名为store的唯一id,值为store的所有状态数据

ts
import { defineStore } from 'pinia'; export default defineStore( 'userinfoStore', () => { const username = ref(''); const rolesname = ref(''); const token = ref(''); return { username, rolesname, token }; }, { persist: { enabled: true, // 开启数据缓存 strategies: [ { key: 'userinfo', // 存储的字段名的key storage: sessionStorage, // 存储方式 localStorage、sessionStorage paths: ['username', 'rolesname', 'token'], // 需要持久化的字段名 }, ], }, } );

OptionsStore中使用

默认情况

  • LoginAndRegister.ts中使用

默认是sessionStorage存储,键名为store的唯一id,值为store的所有状态数据

import { defineStore } from 'pinia'; export const userInfoStroe = defineStore('userinfo', { state: () => { return { username: '', token: '', }; }, persist: { enabled: true, // 开启数据缓存 strategies: [ { storage: localStorage, // 存储方式 localStorage、sessionStorage paths: ['username', 'token'], // 需要持久化的字段名 }, ], }, });

修改State

直接修改

在确保没有丢失响应性时,可以直接通过修改

vue
<template> <div> <div> <input type="text" v-model="name" /> <p>{{ name }}</p> </div> <div> <input type="text" v-model="phone" /> <p>{{ phone }}</p> </div> <div> <button @click="changeName">直接赋值修改name</button> </div> </div> </template> <script setup lang="ts"> import userinfoStore from '@/stores/userinfo.js'; import { storeToRefs } from 'pinia'; const userStore = userinfoStore(); const { name, phone } = storeToRefs(userStore); function changeName(){ name.value = 'xm' } </script>

批量修改

说明

通过.$patch()实现,可以接受一个对象修改部分state,或者接受一个函数批量修改集合内部分对象的情况

如果直接传入一个对象,则对于state中的任何集合修改(EG:数组中推送、删除、拼接元素)都需要创建一个新集合

vue
<template> <div> <div> <input type="text" v-model="name" /> <p>{{ name }}</p> </div> <div> <input type="text" v-model="phone" /> <p>{{ phone }}</p> </div> <div> <button @click="changeName">直接赋值修改name</button> <button @click="changePatchObj">patchObj修改</button> <button @click="changePatchFn">patchFn修改</button> <button @click="resertUser">重置</button> </div> </div> </template> <script setup lang="ts"> import userinfoStore from '@/stores/userinfo.js'; import { storeToRefs } from 'pinia'; const userStore = userinfoStore(); const { name, phone } = storeToRefs(userStore); function resertUser() { userStore.$reset(); } function changeName() { name.value = 'xm'; } function changePatchObj() { userStore.$patch({ name: 'xm', phone: '123344', }); } function changePatchFn() { userStore.$patch((state)=>{ console.log('state', state) // state.name = 'xk' }); } </script>

替换State

  • 通过store.$state设置为新对象来替换整个state的状态
  • 通过store.state.value设置为新对象替换整个state的状态
ts
userStore.$state = {name:'xm',phone:'1212'} userStore.state.value = {name:'xm',phone:'1212'}

重置State

通过.$reset()实现

OptionsStore

说明

OptionsStore中可以直接使用

SetupStore

说明

在组合式API创建的Store中不能直接使用,会报错

ts
Uncaught Error: 🍍: Store "userinfo" is built using the setup syntax and does not implement $reset(). at Proxy.$reset (pinia.mjs:1326:27) at resertUser (AboutView.vue:80:2) at callWithErrorHandling (runtime-core.esm-bundler.js:173:22) at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:182:21) at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js:345:9)

解决方案

自定义pinia插件重写$reset方法,需要借助lodash.clonedeep深度克隆

  1. 安装插件lodash.clonedeep
ts
pnpm install lodash.clonedeep -S
  1. utils/common/storeUtils/创建StoreReset.ts文件
ts
import cloneDeep from 'lodash.clonedeep'; export default function storeReset({ store }) { // 深度克隆初始状态 const initialState = cloneDeep(store.$state); // 将`$reset`方法挂载到store实例上,然后通过store.$patch批量修改state store.$reset = () => store.$patch(cloneDeep(initialState)); }
  1. store/index.ts中注册插件
ts
import storeReset from '@/utils/common/storeUtils/StoreReset'; import { createPinia } from 'pinia'; const store = createPinia(); // 注册插件 store.use(storeReset); export default store;

订阅State变化

说明

可以通过.$subscribe()方法或vuewatch()方法

特点

.$subscribe()的优点: subscriptions只会在patches之后触发一次(例如,当使用上面的函数版本时) ---暂未理解,个人尝试好像都会触发

默认情况下绑定的组件被卸载后,如果需要在组件卸载后保留,需要使用{ detached: true }作为.$subscribe的第二个参数传入

EG

vue
<template> <div> <div> <input type="text" v-model="name" /> <p>{{ name }}</p> </div> <div> <input type="text" v-model="phone" /> <p>{{ phone }}</p> </div> <div> <button @click="changeName">直接赋值修改name</button> <button @click="changePatchObj">patchObj修改</button> <button @click="changePatchFn">patchFn修改</button> <button @click="resertUser">重置</button> </div> </div> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; import userinfoStore from '@/stores/userinfo.js'; import { storeToRefs } from 'pinia'; const userStore = userinfoStore(); const { name, phone } = storeToRefs(userStore); function resertUser() { userStore.$reset(); } function changeName() { name.value = 'xm'; } function changePatchObj() { userStore.$patch({ name: 'xm', phone: '123344', }); } function changePatchFn() { userStore.$patch((state)=>{ console.log('state', state) }); } userStore.$subscribe((mutation, state) => { console.log('mutation, state', mutation, state) // import { MutationType } from 'pinia' /* mutation.type 'direct': 直接修改 'patch object': 通过`$patch()`传入参数为Object 'patch function': 通过`$patch()`传入参数为函数 */ // 与 cartStore.$id 相同 mutation.storeId // 'userinfo' // 只在 mutation.type === 'patch object' 才具备 mutation.payload // 每当它发生变化时,将整个状态持久化到本地存储 localStorage.setItem('test1', JSON.stringify(state)) }) watch( userStore.$state, (state) => { console.log('watch ', state) // 每当它发生变化时,将整个状态持久化到本地存储 localStorage.setItem('test2', JSON.stringify(state)) }, { deep: true } ) </script>
  • 直接修改

subscribeDirect

  • $patch传入对象

subscribeObj

  • $patch传入函数

subscribeFn

本文作者:RKLS

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!