1.0.3 • Published 6 months ago

vue3-persist-storages v1.0.3

Weekly downloads
-
License
ISC
Repository
github
Last release
6 months ago

vue3-persist-storages

前言

众所周知,前端的存储方案主要是以下几种组成:

  • Cookies
  • LocalStorage、SessionStorage
  • indexedDB
  • Web SQL
  • SQLite(小型数据库)

它们之间各自有相应的API,存在差异性比较大,在实际业务开发中,几乎有可能几种存储方案都会使用,他们各自有各自的优缺点,想要进一步了解的,请参考文章:数据持久化存储方案,这里不做过多赘叙,在相同的产品中,对每个持久化存储的方式都有相同需求,于是就有了vue3-persist-storages的出现,目标是就是创造相同api,封装相同的功能,兼容各种实际场景,抹平不同存储的差异化调用。

特点

  • ✅ 囊括了CookiesLocalStorageSessionStorageindexedDBWebSQL所有存储方案,可以自定义选择
  • ✅ 支持普通对象和代理对象proxy的存储
  • ✅ 支持本地持久化存储的有效期设置
  • ✅ 支持通过配置前缀、后缀,防止父子项目之间持久化数据互相污染
  • ✅ 支持本地存储的数据自定义配置AES加密解密
  • ✅ 封装了pinia plugin 插件,支持pinia 的持久化存储,具体特点如下:
    • vuex-persistedstate 相似的 API
    • 所有 Store 均可单独配置,也可以全局设置
    • 恢复持久化数据前后的 hook
    • 同样支持有效期数据加密和解密自定义storage设置

安装

// npm
npm i vue3-persist-storages
// yarn
yarn add vue3-persist-storages
// pnpm
pnpm i vue3-persist-storages

使用Cookies

cookies的存储主要采用js-cookie作为解决方案

CreateCookies API

export declare class CreateCookies {
    /** 前缀,会自动加到 key 值的前面,默认值:'' */
    private prefix;
    /** 后缀,会自动加到 key 值的后面,默认值:''*/
    private suffix;
    /** 是否开启有效期设置,默认值:false */
    private isOpenExpires;
    /** 有效期设置,和isOpenExpires配搭使用,单位:天, 默认值:7 天 */
    private day;
    /** 是否开启AES加密和解密数据, 默认值:false */
    private encryption;
    /** 加密时需要的密钥,自动生成,不需要设置 */
    private encryptionKey;
    constructor(option?: IStorageOption);
    /**
     * @description 设置cookie
     * @param {string} name cookie 名称
     * @param  {any} value cookie 值
     * @param {number} expire 过期时间, 单位:day
     * @param {boolean} isOpen 是否打开过期时间的设置
     */
    setItem(key: string, value: any, expires?: number | null, isOpen?: boolean | null): void;
    /**
     * 根据名字获取cookie值
     * @description Cookies.get()取不到值的时候会返回空字符串 ’‘
     * @param name 键名
     */
    getItem(key: string): string;
    /**
     * 根据名字删除指定的cookie
     * 注意,删除不存在的cookie不会报错也不会有返回
     * @param key
     */
    removeItem(key: string): void;
}

CreateCookies 基本用法

import { CreateCookies } from 'vue3-persist-storages'

const cookies = new CreateCookies()
//  普通使用 string/boolean/number 基本类型
cookies.setItem('name', '张三')
console.log(cookies.getItem('name')) // 输出 张三

// 保存对象,直接传入即可
cookies.setItem('name', {id: '张三'})

// 可以开启有效期、数据加密、前后缀防止数据污染
const cookies = new CreateCookies({
  prefix: '前缀',
  suffix: '后缀',
  isOpenExpires: true,
  day: 0.5,
  encryption: false,
})

使用Storage

Storage 分为LocalStorage、SessionStorage,两者的api完全一样,可以直接使用

CreateStorage API

import { IStorageOption } from './types';
export declare class CreateStorage {
    /** 默认值: localStorage*/
    private storage;
    /** 前缀,会自动加到 key 值的前面,默认值:'' */
    private prefix;
    /** 后缀,会自动加到 key 值的后面,默认值:''*/
    private suffix;
    /** 是否开启有效期设置,默认值:false */
    private isOpenExpires;
    /** 有效期设置,和isOpenExpires配搭使用,单位:天, 默认值:7 天 */
    private day;
    /** 是否开启AES加密和解密数据, 默认值:false */
    private encryption;
    /** 加密时需要的密钥,自动生成,不需要设置 */
    private encryptionKey;
    constructor(option?: IStorageOption);
    /**
     * @description 设置缓存
     * @param key 缓存键
     * @param value 缓存值
     * @param expires 到期时间
     * @param isOpen 是否开启到期时间的计算
     */
    setItem(key: string, value: any, expires?: number | null, isOpen?: boolean | null): void;
    /**
     * @description 读取缓存
     * @param {string} key 缓存键
     * @param {any} def 默认值,不传的话默认值为null
     * @returns 返回缓存值,如果超出有效期,则返回null或者用户自定义的默认值
     */
    getItem(key: string, def?: any): any;
    /**
     * @description 从缓存删除某项
     * @param {string}  key  缓存键
     */
    removeItem(key: string): void;
    /**
     * @description 清空所有缓存
     */
    clear(): void;
}
export default CreateStorage;

CreateStorage 基本用法

基本使用和cookie的方法一样,CreateStorage只是多了一个storage属性,可以供开发选择sessionStorage还是localStorage的存储方式

import { CreateStorage } from 'vue3-persist-storages'

const storage = new CreateStorage()
//  普通使用 string/boolean/number 基本类型
storage.setItem('name', '张三')
console.log(storage.getItem('name')) // 输出 张三

// 保存对象,直接传入即可
storage.setItem('name', {id: '张三'})

// 可以开启有效期、数据加密、前后缀防止数据污染
const storage = new CreateCookies({
  storage: sessionStorage,
  prefix: '前缀',
  suffix: '后缀',
  isOpenExpires: true,
  storage: ,
  day: 0.5,
  encryption: false,
})

使用indexedDB

indexedDB的API相对更难一些,为了更方便使用和入门,vue3PersistStorages采取了localForage作为接入indexedDB的解决方案,localForage只需要通过简单类似 localStorage API 的异步存储来改进你的 Web 应用程序的离线体验,它能存储多种类型的数据,而不仅仅是字符串,针对浏览器进行了兼容处理,当浏览器无法兼容indexedDB会自动降级使用localStorage进行存储。它还有一个优势,就是后续可以拓展方法与sqlite进行联动使用。

CreateLocalForage API

import { IStorageOption } from './types';
export declare class CreateLocalForage {
    private storage;
    private config;
    private lcalForageConfig;
    private waitQueues;
    private debug;
    private encryption;
    private encryptionKey;
    constructor(option?: IStorageOption & LocalForageOptions);
    /**
     * 初始化存储对象
     */
    init(): void;
    /**
     * @description 读取数据库数据
     * @param key 缓存键值
     * @param def 默认值,不传的话默认值为null
     * @returns 本地有数据就那本地数据,没数据就返回默认值
     */
    getItem(key: string, def?: any): Promise<any>;
    /**
      * @description 设置缓存数据
      * @param key 缓存键
      * @param value 缓存值,
      * @member value的类型 支持以下类型
      *  Array
      ArrayBuffer
      Blob
      Float32Array
      Float64Array
      Int8Array
      Int16Array
      Int32Array
      Number
      Object
      Uint8Array
      Uint8ClampedArray
      Uint16Array
      Uint32Array
      String
      */
    setItem(key: string, value: any, _isOpenExpires?: boolean | undefined, expires?: number | undefined): Promise<any>;
    /**
     * @description 从缓存删除某项
     * @param {string}  key  缓存键
     */
    removeItem(key: string): Promise<unknown>;
    /**
     * @description 清空所有缓存
     */
    clear(): Promise<unknown>;
    /**
       * 获取本地数据库的大小,主要是获取indexDB的大小
       * {
          quota: 440922000000, // 最大可用字节数, 浏览器存储空间配额
          usage: 27300000,     // 已用字节数, 浏览器已经使用的存储空间大小,
          usageDetails: {        // 这个返回结果不固定
              indexedDB: 676000, // indexedDB的使用占用空间的大小情况,单位字节,
              cacheapi: 26500000,  // 缓存空间
              serviceworker: 52800  // serviceworker服务
          }
      } */
    getSize(): Promise<string | null>;
}
export default CreateLocalForage;

LocalForageOptions 属性

如果你想单纯只使用LocalForage API的话,可以只配置LocalForageOptions属性,即可跟官方配置一样

属性名类型说明默认值
driverstringstring[]驱动,也就是选择是存储方式localforage.INDEXEDDB, localforage.WEBSQL,localforage.LOCALSTORAGE
sizestring数据库的大小(以字节为单位)。目前仅在 WebSQL 中使用-
versionstring数据库的架构版本。仅在 WebSQL 和 IndexedDB 中使用-
descriptionstring数据库的描述,主要供开发人员使用-
namestring数据库的名称Vue3PersistStorage
storeNamestring数据库建表的名称DataSheet

IStorageOption 属性

如果在希望给LocalForage的使用上,加上有效期设置数据加密解密功能添加前缀后缀防止数据污染,则需要额外添加IStorageOption属性

export interface IPluginOption {
    // 前缀, 默认值: ""
    prefix?: string;
    // 后缀 默认值: ""
    suffix?: string;
    // 数据库名称, 默认值:Vue3PersistStorage
    name?: string;
    // 数据库中表名,默认值:DataShee
    storeName?: string;
    // 调试模式,还原失败打印报错(可选)
    debug?: boolean;
    // 是否开启加密功能
    encryption?: boolean
}

export interface IStorageOption extends IPluginOption {
    // 是否开启有效期, 默认值:false
    isOpenExpires?: boolean;
    // 有效期默认几天, 默认值: 7天
    day?: number;
}

CreateLocalForage 使用

import { CreateLocalForage } from 'vue3-persist-storages'

// 直接使用localforage的api和相关方法
const localforage = new CreateLocalForage({
  name:'test',
  storeName: 'sheet',
  driver: [localforage.LOCALSTORAGE],
})
// 生成的localforage对象,会直接存放在`CreateLocalForage`的storages属性中,这样可以随时调用,CreateLocalForage没有封装过的关于localforage api的方法,
// 关于localforage更具体的api,可以参考: https://localforage.docschina.org/
localforage.storage.keys((key: any) => { console.log(key); })


// 可以开启有效期、数据加密、前后缀防止数据污染
const localforage = new CreateLocalForage({
  prefix: '前缀',
  suffix: '后缀',
  storage: localStorage,
  isOpenExpires: true,
  day: 0.5,
  encryption: false,
})
localforage.setItem('name', { id: 1 })
console.log(localforage.getItem('name'))

// 可以为单个键值为name,独立设置数据有效期为0.5天,其他数据不设有效期
localforage.setItem('name', { id: 1 }, true, 0.5)

使用Pinia Plugin 持久化存储

市场上pinia常用的持久化存储方案主要有以下两种: 1. pinia-plugin-persistedstate,最主流 2. pinia-plugin-persist

以上两种方案在实际使用过程中,都发现以下不足:

  • 不支持配置前缀后缀
  • 不支持indexedDB,可能indexedDB是异步储存
  • 不支持配置数据有效期
  • 不支持配置数据加密和解密

于是才有了vue3-persist-storages,这里的Pinia Plugin是基于CreateLocalForage、CreateCookies、CreateStorage等三种方法而集成使用的。

初始化

// 直接配置插件
import { createPinia } from 'pinia'
import piniaPluginPersisted from 'vue3-persist-storages'

const pinia = createPinia()
pinia.use(piniaPluginPersisted)

全局配置

如果你不想要同域名下子项目持久化数据之间互相污染,可以全局配置前缀或后缀、数据库名、表名、是否开启调试、是否开启加密

export interface IPluginOption {
    // 前缀, 默认值: ""
    prefix?: string;
    // 后缀 默认值: ""
    suffix?: string;
    // 数据库名称, 默认值:Vue3PersistStorage
    name?: string;
    // 数据库中表名,默认值:DataShee
    storeName?: string;
    // 调试模式,还原失败打印报错(可选)
    debug?: boolean;
    // 是否开启加密功能
    encryption?: boolean
}

具体使用:

import { createPinia } from 'pinia'
import {createPlugin} from 'vue3-persist-storages'

const pinia = createPinia()
pinia.use(createPlugin({
  // 前缀
   prefix: '子项目id', 
   // 开启数据加密
   encryption: true,
   // 开启调试
   debug: true
   // 全局配置默认数据库名
   name: 'TestDataBase'
}))

模块配置

模块想要启用持久化,可以配置 persist 参数,它的类型是 TPersist

// 全局的配置,单个模块也可以继承,重新配置
export interface IPluginOption {
    // 前缀, 默认值: ""
    prefix?: string;
    // 后缀 默认值: ""
    suffix?: string;
    // 数据库名称, 默认值:Vue3PersistStorage
    name?: string;
    // 数据库中表名,默认值:DataShee
    storeName?: string;
    // 调试模式,还原失败打印报错(可选)
    debug?: boolean;
    // 是否开启加密功能
    encryption?: boolean
}

export interface IStorageOption extends IPluginOption {
    // storage类型,有localStorage、sessionStroage(可选),可配合type使用
    storage?: Storage;
    // 是否开启有效期, 默认值:false
    isOpenExpires?: boolean;
    // 有效期默认几天, 默认值: 7天
    day?: number;
}

export interface PersistOptions extends IStorageOption {
    // 使用 indexedDB 或 storage(可选)
    type?: 'storage' | 'indexedDB' | 'cookies';
    // 持久化存储的key(可选)
    key?: string;
    // 需要持久化的数据的路径(可选)
    paths?: string[];
    // 还原前执行函数(可选)
    beforeRestore?: (context: PiniaPluginContext) => void;
    // 还原后执行函数(可选)
    afterRestore?: (context: PiniaPluginContext) => void;
}

export type TPersist = boolean | PersistOptions | PersistOptions[];

当你要启用持久化时,可以这么做

export const useUserStore = defineStore({
  id: 'user',
  persist: true, // 开启持久化
  state: () => ({
    name: 'caoguanjie'
  })
});

当你设置 persist = true 时,此模块开启了持久化功能,相当于传入了默认配置:

 persist: {
    type: 'indexedDB',
    name: 'Vue3PersistStorage'
    storeName: 'DataSheet'
    key: $store.id, // 此模块的默认id
    paths: undefined,
    encryption: false,
    beforeRestore: undefined,
    afterRestore: undefined,
    debug: false,
  }

当然,你也可以传入persist相关对象属性,进行配置,如下:

export const useUserStore = defineStore({
  id: 'user',
  persist: {
      type: 'indexedDB',
      name: 'TestDataBase'
      storeName: 'DataSheet'
      key: 'customKey', // 自定义id,不传就是默认模块id
      paths: ['name'],
      encryption: true,
      beforeRestore: () =>{
        console.log('beforeRestore');
      },
      afterRestore: () => {
        console.log('afterRestore');
      },
      debug: false
  }, // 开启持久化
  state: () => ({
    name: 'caoguanjie'
  })
});

如果遇到模块不同的变量需要用不同的存储方式时,你也可以传入数组结构的数据,如下方法:

persist: [
    {
      type: 'indexedDB',
      encryption: true, // 用户信息可以设置加密
      paths: ["userInfo", "isRememberme", "loginInfo"],
    },
    {
      key: 'accessToken',
      type: 'cookies', // token可以cookies保存,并且可以自定义设置键名为accessToken
      paths: ["token"],
    },
  ],

实现自定义存储方式

当使用pinia持久化时,发现以上所有存储方式都无法不合适时,你可以采取自定义的方式,定制数据的提取和存储,例如,要独立实现token字段存储,可以通过以下配置:

persist: [
    {
      type: 'indexedDB',
      encryption: true, // 用户信息可以设置加密
      paths: ["userInfo", "isRememberme", "loginInfo"],
    },
    {
      key: 'accessToken',
      type: 'custom', // token可以js-cookies保存,并且可以自定义设置键名为accessToken
      paths: ["token"],
      // storage的格式一定要按这个模式写setItem、getItem函数
      storage: {
        setItem(key: string, state: any) {
          // 默认有效期是7天
          const _state = JSON.parse(state);
          return Cookies.set("accessToken", _state.token);
        },
        getItem(key: string) {
          return JSON.stringify({
            token: Cookies.get("accessToken"),
          });
        },
      }
    },
  ],

已知的问题

  1. 使用CreateLocalForage、CreateCookies、CreateStorage中的setItem方法,支持单独为某个键值的存储数据,设置有效时间,但是无法为单个数据设置加密和解密。
  2. pinia的store.$subscribe方法是监控所有模块的对象数据变化的,因此当数据量大的时候,复杂对象的监控会极其消耗性能,甚至造成卡顿的情况,在实际场景中,发现使用indexedDB存储数据超过100MB的时候,每次数据的保存,都会有卡顿的情况。性能差的电脑和手机端尤为明显。
  3. 为了兼容pinia的Options APIComposition API两种写法产生的对象不同,Vue3PersistStorage用了JSON.stringify序列化和JSON.parse反序列化来处理对象,在一定程度上会造成多余的性能开销
  4. Vue3PersistStorage不支持vue2SSR