zoukankan      html  css  js  c++  java
  • 页面刷新vuex数据消失问题解决方案

    VBox持续进行中,哀家苦啊,有没有谁给个star。

    vuex是vue用于数据存储的,和redux充当同样的角色。

    最近在VBox开发的时候遇到的问题,页面刷新或者关闭浏览器再次打开的时候数据归零。这是头疼的问题。

    网上搜,大家的方案都是把数据转移到 localStorage或者其他持久化存储(例如indexDB)。

    这倒是可以,我在设计之初因为匆忙,没有考虑周全,这下好,然不成每个 mutation都去存一下。

    这个弄的我很不开心,周六在公司,本来就困的要死,又想不到合理的解决方案,昏昏沉沉睡着了。

    醒了后,最初想采用 柯里化和高阶函数来解决这个问题,很可惜,没有正解。

    最小化修改,又不想动现有代码,代理二字最为不过。记得上次我写IBook之初,也用Proxy来拦截修改,同时存数据到磁盘文件。

    没错方案就是 ES6的Proxy,尝试之后,确实是可以的。

    源码地址:https://github.com/xiangwenhu/vbox/tree/master/src/utils

    这里有两个问题

    1. 初始值的问题。

    2. 我要可以配置哪些字段需要持久化,store里面的数据,不代表我都需要持久化。

    首先解决是 localStorage存储的问题,因为需要转换字符串,简单封装一个 LStorage.js,当然你也可以用 https://github.com/tsironis/lockr , https://github.com/nbubna/store 或者你喜欢的,小轮子我就自己写了。

    复制代码
    const ls = window.localStorage
    // https://github.com/tsironis/lockr
    export default {
      getItem(key) {
        try {
          return JSON.parse(ls.getItem(key))
        } catch (err) {
          return null
        }
      },
      setItem(key, val) {
        ls.setItem(key, JSON.stringify(val))
      },
      clear() {
        ls.clear()
      },
      keys() {
        return ls.keys()
      },
      removeItem(key) {
        ls.removeItem(key)
      }
    }
    复制代码

    其次就是代理的简单封装,LSproxy.js

    这个版本还是有问题的,现在只能代理二级属性,对现在的我而言已经是够用了的。

    createHanlder 创建二级属性的代理

    copy 复制对象,当然你可以写更加兼容优雅的方法

    proxy  创建state的代理

    复制代码
    import LStorage from './LStorage'
    
    /**
     * 代理二级属性
     * @param {*} lsKey 存在localStorage的key
     * @param {*} pk    一级属性的key
     */
    function createHanlder(lsKey, pk) {
      return {
        set: function (target, key, value, receiver) {
          let item = LStorage.getItem(lsKey)
          if (item && item[pk]) {
            item[pk][key] = value
            LStorage.setItem(lsKey, item)
          }
          return Reflect.set(target, key, value, receiver)
        }
      }
    }
    
    /**
     * 仅仅存需要存放的数据
     * @param {*} source 
     * @param {*} keys 
     */
    function copy(source, keys = []) {
      if (!source) {
        return source
      }
      let d = Object.create(null)
      keys.forEach(k => { d[k] = source[k] })
      return d
    }
    
    /**
     * 代理state
     * @param {*} initState 初始化的值
     * @param {*} lsKey  localStorage的key
     * @param {*} keys   需要存储的键
     */
    const proxy = function (initState, lsKey, keys = []) {
      let ks = keys, obj = Object.assign({}, initState, LStorage.getItem(lsKey))
    
      // 代理二级属性
      keys.forEach(k => {
        obj[k] = new Proxy(obj[k], createHanlder(lsKey, k))
      })
      // 存入合并的值
      LStorage.setItem(lsKey, copy(obj, keys))
      return new Proxy(obj, {
        set: function (target, key, value, receiver) {
          ks.indexOf(key) >= 0 && LStorage.setItem(lsKey, copy(target, keys))
          return Reflect.set(target, key, value, receiver)
        }
      })
    }
    
    export { proxy }
    复制代码

    调用这边,基本就没有什么变化, 就多了一句  state = proxy(state, 'playing', ['list'])

    复制代码
    import { proxy } from '../utils/LSProxy'
    let state = {
      list: [],
      current: null
    }
    state = proxy(state, 'playing', ['list'])
    
    const mutations = {
    
      /**
       * 添加歌曲
       * @param {*} state 
       * @param {*} song 歌曲信息 
       */
      addSong(state, song) {
        let index = state.list.findIndex(s => s.songmid === song.songmid)
        if (index < 0) {
          state.list.push(song)
        }
      },
    
      /**
       * 添加歌曲
       * @param {*} state  内置
       * @param {*} songs  歌曲列表
       */
      addSongs(state, songs) {
        let index = -1
        songs.forEach(song => {
          index = state.list.findIndex(s => s.songmid === song.songmid)
          if (index < 0) {
            state.list.push(song)
          }
        })
      },
    
      /**
       * 删除歌曲
       * @param {*} state 
       * @param {*} songmid  歌曲媒体id 
       */
      removeSong(state, songmid) {
        let index = state.list.findIndex(s => s.songmid === songmid)
        index >= 0 && state.list.splice(index, 1)
      },
    
      /**
       * 批量删除歌曲
       * @param {*} state 
       * @param {*} songmids 歌曲媒体列表 
       */
      removeSongs(state, songmids = []) {
        let index = -1
        songmids.forEach(songmid => {
          index = state.list.findIndex(s => s.songmid === songmid)
          index >= 0 && state.list.splice(index, 1)
        })
      },
    
      /**
       * 播放下一首,
       * @param {*} state 
       * @param {*} song 为空
       */
      next(state, song) {
        // 如果song不为空,表示是插放,(前提是已经添加到playing)
        if (song) {
          let index = state.list.findIndex(s => s.songmid === song.songmid)
          if (index >= 0) {
            state.current = state.list[index]
            return
          }
          return
        }
        // 如果current为空,表示没有播放的歌曲
        if (!state.current && state.list && state.list.length > 0) {
          state.current = state.list[0]
          return
        }
        // 如果不是插放,并且current不为空
        if (!song && state.current) {
          // 播放的歌曲是不是在当前的列表
          let index = state.list.findIndex(s => s.songmid === state.current.songmid)
          // 如果在歌曲列表里面,接着播放下首
          if (index >= 0) {
            state.current = (index === state.list.length - 1 ? state.list[0] : state.list[index + 1])
          } else {
            state.current = state.list[0]
          }
        }
      }
    }
    
    export default {
      namespaced: true,
      state,
      mutations
    }
    复制代码

    这种方案的缺点也是很明显的,

    1. 代码只能代理二级,对我一般情况应该是够用了,扁平化state

    2. 代理二级属性和数组,要是属性平凡修改的时候,代理是会重复触发的,比如,添加30首歌曲的时候,是发生了30次存储。 当然我觉得也是有方案可以优化的。

    优点我觉得是,

    1. state的数据与localStorage的同步过程分离开

    2. 对现有代码的注入是相当少的。

    当然我上面代码本身也还是存在问题的

    1. 二级监听不能在proxy执行的时候返回,因为如果属性默认值为null/undefined,或者初始化就没有设置默认值,是不会被监听到的,应该是放到一级属性监听里面, 进行一个判断

    不知道各位大神有什么好的方法,可以分享出来。

    参考文章:

    解决VUEX刷新的时候出现数据消失

  • 相关阅读:
    MySQL锁
    mysql服务性能优化—my.cnf配置说明详解
    springmvc请求参数获取的几种方法
    Linux mysql 添加远程连接
    Linux 操作 mysql
    Linux 安装 mysql 转 http://www.cnblogs.com/fnlingnzb-learner/p/5830622.html
    linux 下 安装nginx
    dubbo 实战总结
    分布式事务的几种方式
    精巧好用的DelayQueue 转
  • 原文地址:https://www.cnblogs.com/wangchaoyuana/p/7497448.html
Copyright © 2011-2022 走看看