前言
由于 vuex
和 pinia
是将数据存储到内存中的,所以刷新页面后数据会丢失。如果想要持久化存储,就需要将数据同步到 WebStorage
。可以使用现有的插件或者自己手写一个插件,本文对二者均有介绍。
其中手写插件案例使用两个简单模块,分别为 count++计数器 和 input输入框,演示如何持久化状态仓库。
vuex
1. 手写插件
自定义一个插件,用于在 组件卸载 时将 state
同步到 WebStorage
store/index.js 使用插件
import Vue from "vue"
import Vuex from "vuex"
import myPlugin from "./plugins/persistent" // 引入自定义插件
import count from "./modules/count"
import text from "./modules/text"
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
count,
text,
},
plugins: [myPlugin], // 使用插件
})
./plugins/persistent.js
const myPlugin = (store) => {
// 在 store 初始化时调用
console.log(store)
}
export default myPlugin
查看参数 store,state为当前仓库状态
考虑到频繁存储对性能的浪费,只需在 beforeunload 时将仓库状态同步到 WebStorage
即可。
const KEY = "VUEX_STORE"
const myPlugin = (store) => {
// 保存仓库数据到本地
window.addEventListener("beforeunload", () => {
localStorage.setItem(KEY, JSON.stringify(store.state))
})
// 恢复仓库数据
try {
const localState = localStorage.getItem(KEY)
if (localState) store.replaceState(JSON.parse(localState))
} catch {
console.log("数据恢复失败")
}
}
export default myPlugin
继续优化,可传入配置项用于自定义key、选择存储localStorage
或sessionStorage
、是否立即同步本地存储
./plugins/persistent.js
const myPlugin = (options) => {
return (store) => {
const { key, immediately, storageType } = options
const KEY = key || "VUEX_STORE"
const storageMap = {
sessionStorage: sessionStorage,
localStorage: localStorage,
}
const STORAGE = storageMap[storageType] || localStorage // 默认使用 localStorage
if (immediately) {
// 仓库数据实时同步到本地存储
store.subscribe((mutation, state) => {
STORAGE.setItem(KEY, JSON.stringify(state))
})
} else {
// 页面刷新时,存储仓库数据
window.addEventListener("beforeunload", () => {
STORAGE.setItem(KEY, JSON.stringify(store.state))
})
}
// 恢复仓库数据
try {
const localState = STORAGE.getItem(KEY)
if (localState) store.replaceState(JSON.parse(localState))
} catch {
console.log("数据恢复失败")
}
}
}
export default myPlugin
使用插件时,传入配置项
plugins: [
myPlugin({
key: "vx-store",
storageType: window.sessionStorage,
immediately: true,
}),
], // 使用插件
2. 使用 vuex-persistedstate 插件
插件地址
下载插件
npm i vuex-persistedstate
store/index.js
// store/index.js
import Vue from "vue"
import Vuex from "vuex"
// import myPlugin from "./plugins/persistent" // 引入自定义插件
import count from "./modules/count"
import text from "./modules/text"
import createPersistedState from "vuex-persistedstate"
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
count,
text,
},
plugins: [createPersistedState()], // 使用插件
})
该插件默认会在每次修改后同步到 localStorage
,key 为 vuex
可以手动传入配置项,如使用 sessionStorage
、key 为 vx_store
plugins: [
createPersistedState({
key: "vx_store", // 存储名称
storage: window.sessionStorage,
}),
], // 使用插件
pinia
1. 手写插件
入口文件,pinia使用插件
import { createApp } from "vue"
import "./style.css"
import App from "./App.vue"
import { createPinia } from "pinia"
import myPlugin from "@/store/plugins/persistent.js"
const pinia = createPinia()
pinia.use(myPlugin) // 使用插件
const app = createApp(App)
app.use(pinia)
plugins/persistent.js
export default function (context) {
console.log(context)
}
查看打印,不同于 vuex,pinia 将每个使用的模块分别打印,需要对每一个模块进行处理
与 vuex 一样,默认只有在页面销毁时同步到本地存储
const KEY_PREFIX = "PINIA-STORE-"
export default function (context) {
const { store } = context
const KEY = KEY_PREFIX + store.$id
// 存
window.addEventListener("beforeunload", () => {
localStorage.setItem(KEY, JSON.stringify(store.$state))
})
// 取
try {
const localState = localStorage.getItem(KEY)
if (localState) {
store.$patch(JSON.parse(localState))
}
} catch {
console.log("读取失败")
}
}
继续优化,可传入配置项包括自定义key前缀、选择存储localStorage
或sessionStorage
、是否立即同步本地存储
const KEY_PREFIX = "PINIA-STORE-"
export default function createPiniaPlugin(options = {}) {
// 解构传入的参数
const {
keyPrefix = KEY_PREFIX,
storageType = "localStorage",
immediately,
} = options
const storageMap = {
localStorage: localStorage,
sessionStorage: sessionStorage,
}
const STORAGE = storageMap[storageType]
// 返回实际的插件函数
return (context) => {
const { store } = context
const KEY = keyPrefix + store.$id
if (immediately) {
// 仓库数据实时同步到本地存储
store.$subscribe((mutation, state) => {
STORAGE.setItem(KEY, JSON.stringify(state))
})
} else {
// 页面刷新时,存储仓库数据
window.addEventListener("beforeunload", () => {
STORAGE.setItem(KEY, JSON.stringify(store.$state))
})
}
// 取出数据
try {
const localState = STORAGE.getItem(KEY)
if (localState) {
store.$patch(JSON.parse(localState))
}
} catch {
console.log("读取失败")
}
}
}
pinia.use(
myPlugin({
keyPrefix: "MY-STORE-", // 自定义的前缀
storageType: "sessionStorage", // 使用 sessionStorage
immediately: true,
})
) // 使用本地持久化插件
2. 使用 pinia-plugin-persistedstate 插件
官方地址
下载插件
npm i pinia-plugin-persistedstate
使用插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' //引入持久化插件
pinia.use(piniaPluginPersistedstate)
创建 Store 时,将 persist
选项设置为 true,整个 Store 将使用默认持久化配置保存。
默认配置:
- 使用 localStorage 存储
- key 为 store.$id
- 存储整个 state
import { defineStore } from "pinia"
export const useCountStore = defineStore("count", {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++
},
},
persist: true, // 默认配置
})
只对 count 这个仓库做了持久化处理,input输入框对应的 text 仓库并没有持久化
自定义配置项
- key:自定义key
- storage:存储方式
- pick:选择存入哪些字段
import { defineStore } from "pinia"
export const useCountStore = defineStore("count", {
state: () => ({
count: 0,
name: "田本初",
}),
actions: {
increment() {
this.count++
},
},
persist: {
key: "PINIA-CountStore",
storage: sessionStorage, // 存储方式
pick: ["count"], //指定 state 中哪些数据需要被持久化。[] 表示不持久化任何状态,undefined 或 null 表示持久化整个 state
},
})
可以发现只有count仓库采用持久化、key为自定义、采用 sessionStorage
存储并且只存入了count