更适合 Vue3 的持久化数据(localStorage)管理库 Prorage

你是否阅读过《你可能不需要 Vuex》或是《你可能不需要 Pinia》?使用 reactive 创建一个全局变量代替 VuexPinia,作为全局状态。个人非常喜欢这么做,但没有与之配套的持久化数据方案,于是就产生了 Prorage

Prorage 简介

项目地址:aweikalee/prorage

Prorage = Proxy + Storage

localStorage 使用起来像对象一样自然。

基于 @vue/reactivity 实现的持久化数据管理,与 reactive 有着几乎相同的使用方式。与 @vue/reactivity 一样可以脱离 Vue 单独使用。

提供了插件系统,可以实现大部分定制化的需求。内置了有效期、数据转换的插件。

快速上手

Playground

Stackblitz

安装

1
2
npm install @vue/reactivity
npm install prorage

如果你已经安装了 Vue,则不需要再安装 @vue/reactivity

使用

1
2
3
4
5
6
7
8
9
import { createStorage, expiresPlugin } from 'prorage'

const storage = createStorage()

storage.foo = 'foo'
delete foo

storage.bar = []
storage.bar.push('hello')

特性

更丰富的数据类型支持

localStorage 只支持字符串,通常会使用 JSON.stringify/JSON.parse 对数据处理。

ProrageJSON.stringify/JSON.parse 基础上,以插件(translatePlugin)的形式提供了更多的类型支持。

基础类型支持情况对比:
| 数据类型 | localStorage | JSON.stringify | Prorage with translatePlugin |
| :-: | :-: | :-: | :-: |
| undefined | ✔️ | ✔️ | ✔️ |
| null | ✔️ | ✔️ | ✔️ |
| String | ❌ | ✔️ | ✔️ |
| Boolean | ❌ | ✔️ | ✔️ |
| Number | ❌ | ✔️,但不支持 NaN/Infinity/-Infinity | ✔️ |
| BigInt | ❌ | ❌ | ✔️ |
| Symbol | ❌ | ❌ | 可以支持 Symbol.for (需用户配置) |

此外还增加了 Date, RegExp 的支持。如果还不满足,则可以通过 translatePlugin 设置更多的类型支持。

Set/Map 实现成本与收益不匹配,并未支持。而 WeakSet/WeakMap 则因实现没有意义,也未支持。

有效期

localStorage 不支持设置数据有效期。

ProrageexpiresPlugin 插件则提供了设置有效期的支持。

1
2
3
4
5
6
7
8
9
10
11
import { createStorage, expiresPlugin, useExpires, getExtra } from 'prorage'
const storage = createStorage({
plugins: [expiresPlugin()]
})

storage.foo = useExpires('bar', { days: 7 })

console.log(storage.foo) // 'bar'

// 7天后
console.log(storage.foo) // undefined

和通常的有效期方案不同的是,Prorage 中存在两种有效期检查,一是在数据被读取时检查,二是设置了定时器定期检查,过期的数据将会被拦截/删除。这使得 Prorage 能更好得配合前端框架,及时更新视图。

定制化

Prorage 提供了较大的定制空间。

1
2
3
4
5
6
7
const storage = createStorage({
storage: localStorage,
stringify: JSON.stringify,
parse: JSON.parse,
prefix: 'prefix#',
plugins: [expiresPlugin()]
})
参数说明
storage储存对象 可替换为 sessionStorage 或是其他 StorageLike
stringify转换为 JSON 字符串的方法
parse解析 JSON 字符串的方法
prefix储存键名前缀
plugins插件

Plugin 插件

插件可声明一系列 Hook,在特定时机被调用。通过插件,可以实现大部分定制化的需求。

插件的详细说明请看文档。

循环引用

可以借助 flatted 之类的 JSON 库来解决循环引用的问题.

1
2
3
4
5
6
7
8
9
10
import { stringify, parse, } from 'flatted'
import { createStorage } from 'prorage'

const storage = createStorage({
stringify,
parse,
})

storage.test = {}
storage.test.circular = storage.test

与 Vue 一起使用

Prorage 完全可以当做 reactive 对象使用。

1
2
3
4
5
6
7
8
9
import { watch, computed } from 'vue'
import { createStorage } from 'prorage'

const storage = createStorage()

const foo = computed(() => storage.foo)
watch(() => storage.bar, (value) => {
console.log(`[bar changed]: ${value}`)
})

与 React 一起使用

就和 @vue/reactivity 在 React 中使用一样,以下是一种简单的使用示例:Prorage With React - StackBlitz

写在后面

如果你也不爱使用 Vuex/Pinia,不妨试试使用 Prorage 管理持久化数据。

当然如果你的项目使用了 Vuex/Pinia,那还是更建议使用配套的持久化数据插件。

你有什么想法或建议,欢迎交流。