用localStorage缓存Redux的state-阿里云开发者社区

开发者社区> deggs7> 正文

用localStorage缓存Redux的state

简介: # 问题 ## 概念 对于目前普遍的“单页应用”,其中的好处是,前端可以从容的处理较复杂的**数据模型**,同时基于数据模型可以进行变换,实现更为良好的交互操作。 良好的交互操作背后,其实是基于一个对应到页面组件状态的模型,随便称其为**UI模型**。 数据模型对应的是后端数据库中的业务数据,UI模型对应的是用户在浏览器一系列操作后组件所呈现的状态。 **这两个模
+关注继续查看

问题

概念

对于目前普遍的“单页应用”,其中的好处是,前端可以从容的处理较复杂的数据模型,同时基于数据模型可以进行变换,实现更为良好的交互操作。

良好的交互操作背后,其实是基于一个对应到页面组件状态的模型,随便称其为UI模型

数据模型对应的是后端数据库中的业务数据,UI模型对应的是用户在浏览器一系列操作后组件所呈现的状态。

这两个模型不是对等的!

比如下图中这个管控台(不存在所谓的子页面,来进行单页路由的切换,而是一个类似portal的各块组件的切换):

screenshot.png

我们构建的这个单页应用,后端的数据库和提供的接口,是存储和管理数据模型的状态。

但是用户操作管控台中,左侧面板的打开/关闭、列表选中的项目、编辑面板的打开等,这些UI模型的状态均不会被后端记录。

现象

当用户强制进行页面刷新,或者关闭页面后又再次打开时,单页应用虽然能从后端拉取数据记录,但是页面组件的状态已经无法恢复了。

目前,多数的单页应用的处理,就是在页面刷新或重新打开后,抛弃之前用户操作后的状态,进到一个初始状态。(当然,如果涉及较多内容编辑的,会提示用户先保存等等)

但这样,显然是 对交互的一种妥协

方案设计

技术场景

我们的单页应用是基于Redux+React构建。

组件的 大部分状态 (一些非受控组件内部维护的state,确实比较难去记录了)都记录在Redux的store维护的state中。
正是因为Redux这种基于全局的状态管理,才让“UI模型”可以清晰浮现出来。

所以,只要在浏览器的本地存储(localStorage)中,将state进行缓存,就可以(基本)还原用户最后的交互界面了

何时取

先说何时取,因为这块好说。

假设我们已经存下了state,localStorage中就会存在一个序列化后的state对象。

screenshot.png

在界面中还原state,只需要在应用初始化的时候,Redux创建store的时候取一次就可以。

...

const loadState = () => {
  try { // 也可以容错一下不支持localStorage的情况下,用其他本地存储
    const serializedState = localStorage.getItem('state');
    if (serializedState === null) {
      return undefined;
    } else {
      return JSON.parse(serializedState);
    }
  } catch (err) {
    // ... 错误处理
    return undefined;
  }
}

let store = createStore(todoApp, loadState())
...

何时存

保存state的方式很简单:

const saveState = (state) => {
  try {
    const serializedState = JSON.stringify(state);
    localStorage.setItem('state', serializedState);
  } catch (err) {
    // ...错误处理
  }
};

至于何时触发保存,一种简(愚)单(蠢)的方式是,在每次state发生更新的时候,都去持久化一下。这样就能让本地存储的state时刻保持最新状态。

基于Redux,这也很容易做到。在创建了store后,调用subscribe方法可以去监听state的变化。

// createStore之后

store.subscribe(() => {
  const state = store.getState();
  saveState(state);
})

但是,显然,从性能角度这很不合理(不过也许在某些场景下有这个必要)。所以机智的既望同学,提议只在onbeforeunload事件上就可以。

window.onbeforeunload = (e) => {
  const state = store.getState();
  saveState(state);
};

所以,只要用户刷新或者关闭页面时,都会默默记下当前的state状态。

何时清空

一存一取做到后,特性就已实现。版本上线,用户使用,本地缓存了state,当前的应用毫无问题。

但是当再次发布新版本代码后,问题就来了。
新代码维护的state和之前的结构不一样,用户用新的代码,读取自己本地缓存的旧的state,难免会出错。
然而用户此时无论怎么操作,都不会清楚掉自己本地缓存的state(不详细说了,主要就是因为上面loadState和saveState的逻辑,导致。。。错误的state会一直被反复保存,即使在developer tools中手动清除localStorage也不会有效果)

解决就是,state需要有个版本管理,当和代码的版本不一致时,至少进行个清空操作。
目前项目中,采用的以下方案:

直接利用state,在其中增加一个节点,来记录version。即增加对应的action、reducer,只是为了维护version的值。

...
// Actions
export function versionUpdate(version = 0.1) {
  return {
    type    : VERSION_UPDATE,
    payload : version
  };
}
...

保存state的逻辑改动较小,就是在每次保存的时候,要把当前代码的version更新到state。

...
window.onbeforeunload = (e) => {
  store.dispatch({
    type: 'VERSION_UPDATE',
    payload: __VERSION__  // 代码全局变量,随工程配置一起处理即可。每次涉及需要更新state的时候,必须更新此版本号。
  })
  const state = store.getState();
  saveState(state);
}
...

读取state的时候,则要比较代码的版本和state的版本,不匹配则进行相应处理(清空则是传给createStore的初始state为undefined即可)

export const loadState = () => {
  try {
    const serializedState = localStorage.getItem('state');
    if (serializedState === null) {
      return undefined;
    } else {
      let state = JSON.parse(serializedState);
      // 判断本地存储的state版本,如果落后于代码的版本,则清空state
      if (state.version < __VERSION__) {
        return undefined;
      } else {
        return state;
      }
    }
  } catch (err) {
    // ...错误处理
    return undefined;
  }
};

其他参考

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10089 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13892 0
7/17博客园活动浅谈网站架构中缓存的应用所有资料
http://files.cnblogs.com/lovecindywang/%e6%b5%85%e8%b0%88%e7%bd%91%e7%ab%99%e6%9e%b6%e6%9e%84%e4%b8%ad%e7%bc%93%e5%ad%98%e7%9a%84%e5%ba%94%e7%94%a8.
544 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
11893 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
7503 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
7365 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
4508 0
+关注
deggs7
专注数据可视化与前端技术
4
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载