前两天做了个vue的项目,为了避免刷新后数据全部恢复原样,在项目中使用了 vuex-persistedstate 这个持久化插件,在这个项目中需要借助这个插件主要实现的功能是:将需要持久化的数据存储并更新到store中。今天我也来自己写一个简单的持久化插件来实现这个功能。


一 学习写一个Vuex插件

引入Vuex官网的例子:

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:

const myPlugin = store => {// 当 store 初始化后调用store.subscribe((mutation, state) => {// 每次 mutation 之后调用// mutation 的格式为 { type, payload }})
}

然后像这样使用:

const store = new Vuex.Store({// ...plugins: [myPlugin]
})

一切如此简单,关键的一点就是在插件内部通过 store.subscribe() 来监听 mutation。在我们的持久化插件中,就是在这个函数内部对数据进行持久化操作。

二  允许用户选择需要被持久化的数据

首先初始化一个插件的主体函数:

const VuexLastingPlugin = function ({watch: '*',storageKey: 'VuexLastingData'
}) {return store => {}
}

插件当中的watch默认为全选符号 *,允许传入一个数组,数组的内容为需要被持久化的数据的 key 值,如 ['key1', 'key2'] 等。接着便可以去 store.subscribe() 里面对数据进行持久化操作了。

const VuexLastingPlugin = function ({watch: '*'
}) {return store => {store.subscribe((mutation, state) => {let watchedDatas = {}// 如果为全选,则持久化整个 state // 否则将只持久化被列出的 stateif (watch === '*') {watchedDatas = state} else {watch.forEach(key => {watchedDatas[key] = state[key]})}// 通过 localStorage 持久化localStorage && localStorage.setItem(storageKey, JSON.stringify(watchedDatas))})}
}

按照 Vuex 的规范,有且只有通过 mutation 才能够修改 state,于是按照上面的步骤,我们便完成了对数据进行实时持久化的工作。

这里也有一个小问题,就是写入 watch 参数的数组元素必须是state 当中的最外层 key,不支持形如 a. b. c 这样的嵌套形式。功能显然不够完善,所以我们希望可以增加对嵌套 key 的支持。

新建一个工具函数 getObjDeepValue():

function getObjDeepValue (obj, keysArr) {let val = objkeysArr.forEach(key => {val = val[key]})return val
}

该函数接收一个对象和一个 key 值数组, 返回对应的值,我们来验证一下:

var obj = {a: {name: 'aaa',b: {name: 'bbb',c: {name: 'ccc'}}}
}
getObjDeepValue(obj, 'a.b.c'.split('.'))
// => { name: "ccc" }

验证成功以后,便可以把这个工具函数也放进 store.subscribe() 里使用了:

    store.subscribe((mutation, state) => {let watchedDatas = {}if (watch === '*') {watchedDatas = state} else {watch.forEach(key => {// 形如 a.b.c 这样的 key 会被保存为 deep_a.b.c 的形式if (data.split('.').length > 1) {watchedDatas[`deep_${key}`] = getObjDeepValue(state, key.split('.'))} else {watchedDatas[key] = state[key]}})}localStorage && localStorage.setItem(storageKey, JSON.stringify(watchedDatas))})

经过这一改造,通过 watch 写入的 key 值将支持嵌套的形式,整个插件将会更加灵活。

三  从本地读取持久化数据并更新至Store

从上面的步骤我们已经能够灵活监听 store 里的数据并持久化它们了,接下来的工作就是完成如何在浏览器刷新之后去读取本地持久化数据,并把它们更新到 store。

为插件添加一个默认为 true 的选项 autoInit,作为是否自动读取并更新 store 的开关。从功能上来说,刷新浏览器之后插件应该自动读取 localStorage 里面所保存的数据,然后把它们更新到当前的 store。关键的点就是如何把 deep_${key} 的值正确赋值到对应的地方,所以我们需要再新建一个工具函数 setObjDeepValue()

function setObjDeepValue (obj, keysArr, value) {let key = keysArr.shift()if (keysArr.length) {setObjDeepValue(obj[key], keysArr, value)} else {obj[key] = value}
}

该函数接收一个对象,一个 key 值数组,和一个 value,设置对象对应 key 的值,我们来验证一下:

var obj = {a: {name: 'aaa',b: {name: 'bbb',c: {name: 'ccc'}}}
}setObjDeepValue(obj, ['a', 'b', 'c'], 12345)
/**
obj = {a: {name: 'aaa',b: {name: 'bbb',c: 12345}}
}
*/

有了这个工具方法,就可以正式操作 store 了。

 if (autoInit) {const localState = JSON.parse(storage && storage.getItem(storageKey))const storeState = store.stateif (localState) {Object.keys(localState).forEach(key => {// 形如 deep_a.b.c 形式的值会被赋值到 state.a.b.c 中if (key.includes('deep_')) {let keysArr = key.replace('deep_', '').split('.')setObjDeepValue(storeState, keysArr, localState[key])delete localState[key]}})// 通过 Vuex 内置的 store.replaceState 方法修改 store.statestore.replaceState({ ...storeState, ...localState })}}

上面这段代码会在页面初始化的时候读取 storage 的值,然后把形如 deep_a.b.c  的值提取并赋值到 store.state.a.b.c 当中,最后通过 store.replaceState() 方法更新整个 store.state 的值。这样便完成了从本地读取持久化数据并更新至 Store 的功能。

四 案例测试

App.vue

<template><div id="app"><pre>{{$store.state}}</pre><button @click="updateA">updateA</button><button @click="updateX">UpdateX</button></div>
</template><script>
export default {name: 'app',methods: {updateA () {let random = Math.random()this.$store.commit('updateA', {name: 'aaa' + random,b: {name: 'bbb' + random,c: {name: 'ccc' + random}}})},updateX () {this.$store.commit('updateX', { name: Math.random() })}}
}
</script>

Store.js

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPlugin from './vuexPlugin'Vue.use(Vuex)export default new Vuex.Store({plugins: [VuexPlugin({watch: ['a.b.c', 'x']})],state: {a: {name: 'aaa',b: {name: 'bbb',c: {name: 'ccc'}}},x: {name: 'xxx'}},mutations: {updateA (state, val) {state.a = val},updateX (state, val) {state.x = val}}
})

从案例可以看出,我们针对 state.a.b.c 和 state.x 进行了数据持久化。在整个 state.a 都被修改的情况下,仅仅只有 state.a.b.c 被存入了 localStorage ,数据恢复的时候也只修改了这个属性。而 state.x 则整个被监听,所以任何对于 state.x 的改动都会被持久化并能够被恢复。

这个 Vuex 插件仅在浏览器环境生效,未曾考虑到 SSR 的情况。

手写一个Vuex的持久化插件相关推荐

  1. 深入Vue底层,手写一个vuex

    深入底层,手把手教你写一个Vuex 1. Vuex是什么?什么场景下使用? 2. Vuex的基本使用 3. 手写一个vuex 1. Vuex是什么?什么场景下使用? Vuex是vue的一个插件,叫做状 ...

  2. vue 使用fs_模仿vue-cli,手写一个脚手架

    vue-cli 在vue的开发的过程中,经常会使用到vue-cli脚手架工具去生成一个项目.在终端运行命令vue create hello-world后,就会有许多自动的脚本运行. 为什么会这样运行呢 ...

  3. 未能加载文件或程序集或它的某一个依赖项_手写一个miniwebpack

    前言 之前好友希望能介绍一下 webapck 相关的内容,所以最近花费了两个多月的准备,终于完成了 webapck 系列,它包括一下几部分: webapck 系列一:手写一个 JavaScript 打 ...

  4. ElasticSearch——手写一个ElasticSearch分词器(附源码)

    1. 分词器插件 ElasticSearch提供了对文本内容进行分词的插件系统,对于不同的语言的文字分词器,规则一般是不一样的,而ElasticSearch提供的插件机制可以很好的集成各语种的分词器. ...

  5. Java 手写一个SQL分页

    Java手写一个类似PageHelper的分页SQL 目前分页插件众所周知的莫过于和mybatis完美融合的PageHelper了,简单两行代码就实现了sql分页,配合PageInfo类,将数据总数量 ...

  6. 手写一个山寨版的springmvc框架

    文章目录 一,环境准备 二,项目结构搭建 三,简易版的前端控制器 DnDispatcherServlet 四,测试springmvc的性能 五,结束语 首先贴出来一张从网上copy下来的 spring ...

  7. 手写一个http容器【上】决策树与路由表

    Linux之父说过,伪代码是最好的语言,因为它能够表达所有的逻辑.所以本文所有的代码示例都是伪代码. ALFP协议 如果让我来定义http协议的话,我会给他取一个完全不同的名字:ALFP(Applic ...

  8. vue中手写一个放大镜功能

    vue中手写一个放大镜功能 有的时候需要对图片进行放大,类似于电商的商品放大功能,于是在这个想法上写了一个放大镜的功能,并且在放大镜的基础上新添加了一些小功能,下面开始吧! 放大镜是封装的组件的形式, ...

  9. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

最新文章

  1. Nginx 负载均衡器(1+2)
  2. Ruby:字符集和编码学习总结
  3. 阿里花17.7亿 买的才不是ofo一堆没用的单车
  4. JAVA基础知识汇总(思维导图)
  5. 关于笔记本自动睡眠更改设置无用的解决办法
  6. 计算机安全英语文献论文,计算机安全与防护学论文参考文献 计算机安全与防护英语参考文献哪里找...
  7. macbook 虚拟机安装win7
  8. R语言实战——单个总体均值的区间估计
  9. 概率论基础(2)条件概率、全概率公式和贝叶斯公式
  10. 安装 Windows 8 跳过密钥的方法
  11. 基于单片机的自动追日系统设计_基于单片机控制的简易逐日式太阳能发电系统...
  12. 能力值的计算:一年 365 天,以第 1 天的能力值为基数,记为 1.0,当每天好好学习时能力值相比前一天提高 1‰,当没有学习时由于遗忘等原因能力值相比前一天下降 1‰
  13. 打印机与电脑文件服务器,电脑无法共享局域网打印机和文件的解决方法
  14. android 自定义长条进度条_Android 条形进度条
  15. 数据结构-树与深度优先遍历
  16. 讯飞webapi语音合成多音字处理
  17. loadrunner入门教程(6) --新建脚本
  18. 有关圆排列问题——m个相同的元素和n个不同的元素的圆排列解法。
  19. 吹风机用了都说好的戴森,在干手器里也有秘密武器
  20. 如何绘制大气磅礴的水墨字?

热门文章

  1. Halcon中降采样函数
  2. Unity中Bounce Intensity的用法
  3. 《锋迷商城》——用户认证
  4. 最基础_负数二进制转换
  5. 那些年啊,那些事——一个程序员的奋斗史 ——34
  6. [项目管理-24]:非暴力沟通的本质就是:”用大家都舒服的方式解决问题“
  7. 简述php无限极分类,PHP 无限极分类
  8. 说说腾讯分站被黑的过程
  9. Flutter 项目在iPhone/苹果手机启动页/闪屏页黑屏问题
  10. ABeam News | 聚智同行,制胜未来,ABeam Consulting出席2023思爱普中国峰会