# 主文件
import type { StateTree } from 'pinia'
import type { PiniaPlugin } from "pinia"
import type {
PersistedStateFactoryOptions,
PersistedStateOptions,
} from './types'
function get(state: StateTree, path: Array<string>) {
return path.reduce((obj, p) => {
return obj?.[p]
}, state)
}
function set(state: StateTree, path: Array<string>, val: unknown): StateTree {
return (
(path.slice(0, -1).reduce((obj, p) => {
if (!/^(__proto__)$/.test(p))
return (obj[p] = obj[p] || {})
else return {}
}, state)[path[path.length - 1]] = val),
state
)
}
export function pick(baseState: StateTree, paths: string[]): StateTree {
return paths.reduce((substate, path) => {
const pathArray = path.split('.')
return set(
substate,
pathArray,
get(baseState, pathArray)
)
}, {})
}
const isObject = (v: unknown): v is Object => typeof v === 'object' && v !== null
export const normalizeOptions = (
options: boolean | PersistedStateOptions | undefined,
globalOptions: PersistedStateFactoryOptions,
): PersistedStateOptions => {
options = isObject(options)
? options
: Object.create(null)
return new Proxy(options as object, {
get(t, p, r) {
return (
Reflect.get(t, p, r) ||
Reflect.get(globalOptions, p, r)
)
}
})
}
export function piniaStorage(globalOptions: PersistedStateFactoryOptions = {}): PiniaPlugin {
return function (ctx) {
{
const { store, options } = ctx
// @ts-ignore
let { openStorage } = options || {}
if (!openStorage) return
const {
paths = null,
afterRestore,
beforeRestore,
serializer = {
serialize: JSON.stringify,
deserialize: JSON.parse
},
key = store.$id
} = normalizeOptions(openStorage, globalOptions)
beforeRestore?.(ctx)
try {
// @ts-ignore
const fromStorage = localStorage.getItem(store.$id)
if (fromStorage)
store.$patch(serializer.deserialize(fromStorage))
} catch (_error) { }
afterRestore?.(ctx)
store.$subscribe(
(_, state) => {
try {
const toStore = Array.isArray(paths)
? pick(state, paths)
: state
// @ts-ignore
localStorage.setItem(
key,
serializer.serialize(toStore)
)
} catch (_error) { }
},
{ detached: true }
)
}
}
}
1
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 类型定义
import type { PiniaPluginContext, StateTree } from 'pinia'
type Prettify<T> = { [K in keyof T]: T[K] }
export type StorageLike = Pick<Storage, 'getItem' | 'setItem'>
export interface Serializer {
/**
* Serializes state into string before storing
* @default JSON.stringify
*/
serialize: (value: StateTree) => string
/**
* Deserializes string into state before hydrating
* @default JSON.parse
*/
deserialize: (value: string) => StateTree
}
export interface PersistedStateOptions {
/**
* Storage key to use.
* @default $store.id
*/
key?: string
/**
* Dot-notation paths to partially save state. Saves everything if undefined.
* @default undefined
*/
paths?: Array<string>
/**
* Customer serializer to serialize/deserialize state.
*/
serializer?: Serializer
/**
* Hook called before state is hydrated from storage.
* @default null
*/
beforeRestore?: (context: PiniaPluginContext) => void
/**
* Hook called after state is hydrated from storage.
* @default undefined
*/
afterRestore?: (context: PiniaPluginContext) => void
}
export type PersistedStateFactoryOptions = Prettify<Pick<
PersistedStateOptions,
'serializer' | 'afterRestore' | 'beforeRestore'
> & {
/**
* Global key generator, allows pre/postfixing store keys.
* @default storeKey => storeKey
*/
key?: (storeKey: string) => string
/**
* Automatically persists all stores, opt-out individually.
* @default false
*/
auto?: boolean
}>
declare module 'pinia' {
export interface DefineStoreOptionsBase<S extends StateTree, Store> {
openStorage?: boolean | PersistedStateOptions
}
}
1
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 注册
import { createSSRApp } from "vue";
import * as Pinia from "pinia";
import { piniaStorage } from "@/utils/piniaStorage/plugin"; // 我们编写的持久化插件
export function createApp() {
const app = createSSRApp(App);
const store = Pinia.createPinia();
store.use(piniaStorage());
app.use(store);
return {
app,
Pinia,
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用
import { defineStore } from "pinia";
import { guestCarts } from "@/request/api/address/index";
import { countries, countriesID } from "@/request/api/common/index";
// 创建store,命名规则: useXxxxStore
const useInfoStore = defineStore({
id: "useInfo",
state: () => ({
test: "",
}),
getters: {
},
actions: {
setTest(value: any) {
this.test = value;
},
},
openStorage: true,
});
export default useInfoStore;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19