夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 响应式

  • 组件

  • 过渡&动画

  • 可复用性

  • 工具

  • 规模化

  • 路由(核心)

  • 组合式

  • Vuex

  • Pinia

    • Pinia
    • 编写Pinia持久化插件
      • 主文件
      • 类型定义
      • 注册
      • 使用
  • 其他

  • Vue3 源码学习记录

  • 《Vue》笔记
  • Pinia
夜猫子
2023-04-25
目录

编写Pinia持久化插件

# 主文件

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

# 类型定义

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

# 注册

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

# 使用

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
编辑 (opens new window)
上次更新: 2023/8/22 19:22:36
Pinia
手动渲染虚拟dom

← Pinia 手动渲染虚拟dom→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式