使用pinia-plugin-persistedstate如何清除localStorage中的数据
# 概述
vue3前端项目,pinia是数据状态管理的官方标配,而pinia-plugin-persistedstate则是针对pinia库进行数据持久化的插件。
本文主要介绍在使用上述三个库(或插件)时,如何清除持久化后的数据。
# 问题介绍
# 问题来源
某个系统中userStore用于存放用户信息,并且使用了persist持久化用户信息userInfo。编写退出登录逻辑时,在userStore的退出action中重新赋值this.userInfo = {},并使用localStorage直接操作缓存,清除所有缓存数据。打开控制台发现localStorage并中依旧残存userInfo的缓存数据。
# 问题分析
this.userInfo的重新赋值,就是userStore的state发生了变化,这会触发pinia-plugin-persistedstate中的监听,在pinia-plugin-persistedstate中有如下代码:
store.$subscribe(
(_mutation, state) => {
persistState(state, persistence);
},
{
detached: true
}
);
2
3
4
5
6
7
8
如上代码会监听store中state的变化,继而触发persistState方法,而persistState的实现如下:
function persistState(state, { storage, serializer, key, paths, debug }) {
try {
const toStore = Array.isArray(paths) ? pick(state, paths) : state;
storage.setItem(key, serializer.serialize(toStore));
} catch (e) {
if (debug)
console.error("[pinia-plugin-persistedstate]", e);
}
}
2
3
4
5
6
7
8
9
persistState会将需要持久化的state再次存储到storage中。
在pinia的源码中,store的订阅subscribe实现如下:
$subscribe(callback, options = {}) {
const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher());
const stopWatcher = scope.run(() => vueDemi.watch(() => pinia.state.value[$id], (state) => {
if (options.flush === 'sync' ? isSyncListening : isListening) {
callback({
storeId: $id,
type: exports.MutationType.direct,
events: debuggerEvents,
}, state);
}
}, assign({}, $subscribeOptions, options)));
return removeSubscription;
}
2
3
4
5
6
7
8
9
10
11
12
13
store.$subscribe的第一个参数callback回调事件是通过vueDemi.watch监听某个具体的store变化,从而调用的。vueDemi.watch实际上就是vue中的watch方法。而watch方法默认是异步执行的。
即在userStore的action中执行this.userInfo = {},再清除缓存localStorage.clear(),会先改变userStore的state,此时会触发持久化库pinia-plugin-persistedstate中的监听,但是回调函数callback不会立即执行,而是会被放在异步队列中,等下一个事件循环中去执行,因此会先执行同步代码清除缓存localStorage.clear(),最后才是触发callback,在callback中拿到最新的state数据,调用persistState进行数据的再次持久化存储。
# 思考
pinia本身就是用于管理数据的,在实际开发中不提倡手动操作(缓存)数据,而且pinia提供了$reset方法会重置数据。因此可以封装一个统一的store管理工具。其实现如下:
// utils/storeManager.ts
import { useUserStore } from '@/stores/user'
import { useAppStore } from '@/stores/app'
import { useSettingsStore } from '@/stores/settings'
export class StoreManager {
// 清除所有 store 的持久化数据
static clearAllStores() {
const stores = [
useUserStore(),
useAppStore(),
useSettingsStore()
// 添加其他需要清除的 store
]
stores.forEach(store => {
if (store.$reset) {
store.$reset()
}
})
// 可选:手动清除 localStorage 中的残留数据
this.clearPersistedStorage()
}
// 清除特定的一组 store
static clearStores(storeNames: string[]) {
storeNames.forEach(name => {
switch (name) {
case 'user':
useUserStore().$reset()
break
case 'app':
useAppStore().$reset()
break
case 'settings':
useSettingsStore().$reset()
break
// 添加其他 store
}
})
}
// 清除 localStorage 中的持久化数据
private static clearPersistedStorage() {
const prefix = 'your-app-name' // 你的应用前缀
Object.keys(localStorage)
.filter(key => key.includes('-store')) // 根据你的 key 模式过滤
.forEach(key => {
localStorage.removeItem(key)
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 总结
引发问题的核心就是vue.watch的第二个参数回调函数是默认异步执行的,会造成localStorage.clear无法清除缓存的错觉。