单页应用模式下保留上个页面浏览状态实践

890 阅读5分钟

方案一:session

概念

  • localStorage理论上来说是永久有效的,即不主动清空的话就不会消失,即使保存的数据超出了浏览器所规定的大小,也不会把旧数据清空而只会报错。但需要注意的是,在移动设备上的浏览器或各Native App用到的WebView里,localStorage都是不可靠的,可能会因为各种原因(比如说退出App、网络切换、内存不足等原因)被清空。

  • sessionstorage:sessionStorage 属性允许你访问一个 session Storage 对象。它与 localStorage 相似,不同之处在于 localStorage 里面存储的数据没有过期时间设置,而存储在sessionStorage 里面的数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。在新标签或窗口打开一个页面时会在顶级浏览上下文中初始化一个新的会话,这点和 session cookies 的运行方式不同。

weblog.west-wind.com/posts/2013/…

//下面的示例会自动保存一个文本输入框的内容,如果浏览器因偶然因素被刷新了,文本输入框里面的内容会被恢复,因此写入的内容不会丢失。

// 获取文本输入框

let field = document.getElementById("field");

// 检测是否存在 autosave 键值

// (这个会在页面偶然被刷新的情况下存在)

if (sessionStorage.getItem("autosave")) {

// 恢复文本输入框的内容

field.value = sessionStorage.getItem("autosave");

}

// 监听文本输入框的 change 事件

field.addEventListener("change", function() {

// 保存结果到 sessionStorage 对象中

sessionStorage.setItem("autosave", field.value);

})

方案二 React keep-alive

页面缓存

在Vue构建的单页面应用(SPA)中,路由模块一般使用vue-router。vue-router不保存被切换组件的状态,它进行push或者replace时,旧组件会被销毁,而新组件会被新建,走一遍完整的生命周期。

但有时候,我们有一些需求,比如跳转到详情页面时,需要保持列表页的滚动条的深度,等返回的时候依然在这个位置,这样可以提高用户体验。在Vue中,对于这种“页面缓存”的需求,我们可以使用keep-alive组件来解决这个需求。

使用方式

keep-alive是个抽象组件(或称为功能型组件),实际上不会被渲染在DOM树中。它的作用是在内存中缓存组件(不让组件销毁),等到下次再渲染的时候,还会保持其中的所有状态,并且会触发activated钩子函数。因为缓存的需要通常出现在页面切换时,所以常与router-view一起出现:

<keep-alive :include="['ListView', 'DetailView']"> 
    <router-view /> 
</keep-alive>

如此一来,每一个在router-view中渲染的组件,都会被缓存起来。

如果只想渲染某一些页面/组件,可以使用keep-alive组件的include/exclude属性。include属性表示要缓存的组件名(即组件定义时的name属性),接收的类型为string、RegExp或string数组;exclude属性有着相反的作用,匹配到的组件不会被缓存。假如可能出现在同一router-view的N个页面中,我只想缓存列表页和详情页,那么可以这样写:

  • React router 的keep-alive

根据vue router的 keep-alive,搜了一下react-router,是没有这种实现的。但是GitHub上搜到了一个插件 react 的 keep-alive(github.com/StructureBu…

github上该问题的讨论:github.com/facebook/re…

react-keep-alive插件的实践结果

1.KeepAlive放到页面组件外面

在首页input里输入内容,点击排片页link跳转到/movie页面,点击回退,回到首页,输入框中仍有内容。

2.KeepAlive放到页面里的单个组件外面,功能也可以实现

原理浅析

通过 React.createPortal API 实现了这个效果。react-keep-alive 有两个主要的组件 和 ; 负责保存组件的缓存,并在处理之前通过 React.createPortal API 将缓存的组件渲染在应用程序的外面。缓存的组件必须放在 中, 会把在应用程序外面渲染的组件挂载到真正需要显示的位置。

这个组件没有直接使用样式来进行控制,而是使用了 React 16.3 新出的 createPortal API 把组件都挂载到了 App 外面,然后把 DOM 移动到显示的位置来实现的缓存。这样做能够很好的与 React Router 结合,并且也可以在切换页面时使用动画。

看页面react结构图,可以看到在页面最根部元素有创建一个ReactPortal,这个元素不会展示,但是可以缓存组件。结合react-router的path match,来决定是否在页面中展示缓存组件。

方案三 react-router-cache-route

github.com/CJY0208/rea…

react-router 中的 Route 在没有匹配指定 path 时会卸载掉对应的组件,在 match 时重新挂载,component 的生命周期重新开始走,丢失了组件中的数据和交互状态,

插件从 children prop 入手,手动控制 在两种 match 状态下组件的渲染规则,用“隐藏组件”替代“卸载组件”,也就是添加了两个生命周期: didCache didRecover,就可以保留全部的数据和交互状态。最根本的是,react-router-cache-route这个插件可以组织要缓存的组件卸载行为,只是设置display:none而不会被卸载。