@h5plus/persist

基于Vuex的持久化保存,加入了HTML5+ 的 plus.storage 支持

Usage no npm install needed!

<script type="module">
  import h5plusPersist from 'https://cdn.skypack.dev/@h5plus/persist';
</script>

README

persist

基于Vuex的持久化保存,加入了HTML5+ 的 plus.storage 支持,参考了 vuex-persist

安装

npm install @h5plus/persist --save
# or
yarn add @h5plus/persist

使用

步骤

1.导入

import persist from '@h5plus/persist'

2.创建对象

const vuexLocal = new VuexPersistence({
  storage: window.localStorage
})

3.作为 Vuex plugin.

const store = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  plugins: [vuexLocal.plugin]
}
export default vuexStore

构造参数

创建对象时的参数对象 options

属性 类型 说明
key string 在 storage 中存储对应的键值
Default: 'vuex'
delKey string 需要删除 storage 中存储对应的键值,一般用于换库的时候
Default: ''
storage Storage (Web API) localStorage, sessionStorage, localforage 或自定义的存储对象.
必须有 getItem, setItem, clear 等方法.
Default: window.localStorage
plus boolean 是否 HTML5+ API 中的存储对象(同步存储): plus.storage
Default: false
needEvent boolean 是否需要跨窗体的事件通知
Default: true
saveState function
(key, state[, storage])
持久化保存 state,可以自定义
restoreState function
(key[, storage]) => state
从持久化存储中读取 state,可以自定义
reducer function
(state) => object
State reducer. 仅保存需要保存的值。
默认情况下,保存整个 state,如果指定了 modules 则只保存列表中的值;或者自定义
filter function
(mutation) => boolean
Mutation filter. 可以针对 mutation.type 进行过滤
只为那些想要持久化写入的对象触发。
Default 所有的 mutations 返回 true
modules string[] 要持久化的模块列表。(使用它,你可用不用自定义 reducer)
asyncStorage boolean 是否异步存储,意味着 store 使用 Promises (例如 localforage)
Default: false
supportCircular boolean 表示该状态是否具有自身的循环引用 (state.x === state)

使用说明

Reducer

自定义 reducer 不应该改变 state 的结构。

const persist = new VuexPersistence({
  reducer: (state) => state.products,
  ...
})

上面的代码就是错的 wrong
应该这么做:

const persist = new VuexPersistence({
  reducer: (state) => ({products: state.products}),
  ...
})

Circular States

如果你的 state 中有循环引用的结构

let x = { a: 10 }
x.x = x
x.x === x.x.x // true
x.x.x.a === x.x.x.x.a //true

JSON.parse() and JSON.stringify() 将不能正常工作. 你需要安装 circular-json

npm install circular-json

并且在构造 store 时, 添加 supportCircular

new VuexPersistence({
  supportCircular: true,
  ...
})

示例

简单的

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from '@h5plus/persist'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    user: { name: 'Arnav' },
    navigation: { path: '/home' }
  },
  plugins: [new VuexPersistence().plugin]
})

export default store

多实例的

示例 1

import Vue from 'vue'
import VuexPersistence from 'vuex-persist'
import Cookies from 'js-cookie'
import { module as userModule } from './user'
import navModule from './navigation'

Vue.use(Vuex)

const vuexCookie = new VuexPersistence({
  restoreState: (key, storage) => Cookies.getJSON(key),
  saveState: (key, state, storage) =>
    Cookies.set(key, state, {
      expires: 3
    }),
  // 只保存 user 模块
  modules: ['user'],
  // 过滤:只处理类型为 'logIn' 或 'logOut' 的变更
  filter: mutation => mutation.type == 'logIn' || mutation.type == 'logOut'
})
const vuexLocal = new VuexPersistence({
  storage: window.localStorage,
  // 只保存 navigation 模块
  reducer: state => ({ navigation: state.navigation }),
  // 过滤:只处理类型为 'laddNavItem' 的变更
  filter: mutation => mutation.type == 'addNavItem'
})

const store = new Vuex.Store({
  modules: {
    user: userModule,
    navigation: navModule
  },
  plugins: [vuexCookie.plugin, vuexLocal.plugin]
})

export default store

示例 2

import VuexPersistence from '@h5plus/persist'

import localForage from 'localforage'
const vuexLocal = new VuexPersistence({
  // 使用 localForage
  storage: localForage,
  // 只保存 rates 模块
  modules: ['rates'],
  // 过滤:只处理类型为 'rateUpsert' 的变更
  filter: mutation => {
    return mutation.type == 'rateUpsert'
  }
})

const baseData = new VuexPersistence({
  key: 'base',
  // 只保存 'coins', 'config' 模块
  modules: ['coins', 'config'],
  // 过滤:只处理类型为 'coinsUpsert' 或 'walletUpsert' 的变更
  filter: mutation => {
    return mutation.type == 'coinsUpsert' || mutation.type == 'walletUpsert'
  }
})

const recordsData = new VuexPersistence({
  // 使用 plus.storage
  plus: true,
  key: 'records',
  // 只保存 records 模块
  modules: ['records'],
  // 过滤:只处理类型为 'rateUpsert' 的变更
  filter: mutation => {
    return mutation.type == 'recordsUpsert'
  }
})

const vuexStore = new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  plugins: [baseData.plugin, recordsData.plugin, vuexLocal.plugin]
})

export default vuexStore

严格模式

支持 Vuex 严格模式 (注意, 不要在发布环境下启用严格模式!) 在严格模式, 不能使用 store.replaceState ,所以使用一个变更(mutation)来代替

你需要添加 RESTORE_MUTATION 到你的 mutations

看下面示例

配置严格模式支持

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'

const vuexPersist = new VuexPersistence({
  // This **MUST** be set to true
  strictMode: true,
  storage: localStorage,
  reducer: state => ({ dog: state.dog }),
  filter: mutation => mutation.type === 'dogBark'
})

const store = new Vuex.Store({
  // This makes the Vuex store strict
  strict: true,
  state: {
    user: {
      name: 'Arnav'
    },
    foo: {
      bar: 'baz'
    }
  },
  mutations: {
    // 这个变更 **必须** 命名为 "RESTORE_MUTATION"
    RESTORE_MUTATION: vuexPersist.RESTORE_MUTATION
  },
  plugins: [vuexPersist.plugin]
})

一些常用的 store

  • js-cookie 使用浏览器 Cookie
  • window.localStorage (PC 重启也会保留, 知道你清理浏览器数据)
  • window.sessionStorage (关闭浏览器选项卡时消失)
  • localForage 使用浏览器中的 IndexedDB 数据库
  • plus storage Storage 模块管理应用本地数据存储区,用于应用数据的保存和读取

关于 LocalForage 和异步存储的注意事项

HTML5 DOM 规范定义的 Window.Storage API,它实现了以下功能 -

interface Storage {
  readonly length: number
  clear(): void
  getItem(key: string): string | null
  key(index: number): string | null
  removeItem(key: string): void
  setItem(key: string, data: string): void
  [key: string]: any
  [index: number]: string
}

如您所见,它是一个完全同步的存储。 还要注意它仅保存字符串值。 因此,对象被字符串化并存储。

现在看 Local Forage 代表的接口 -

export interface LocalForage {
  getItem<T>(key: string): Promise<T>
  setItem<T>(key: string, data: T): Promise<T>
  removeItem(key: string): Promise<void>
  clear(): Promise<void>
  length(): Promise<number>
  key(keyIndex: number): Promise<string>
  _config?: {
    name: string
  }
}

你可以在这注意到 2 个不同之处-

  1. 所有函数都使用 Promises 来进行异步(因为 WebSQL 和 IndexedDB 是异步的)
  2. 它保存的可以是对象(不仅仅是字符串)

当使用异步(基于 Promise)存储时,您的状态将不会立即从 localForage 恢复到 vuex。它将进入事件循环,并在 JS 线程为空时完成。这可以调用几秒钟的延迟。

参考这个来处理

参考