Vue项目全局配置页面缓存之按需读取缓存的实现详解


Posted in Javascript onAugust 01, 2018

写在前面

一个web app的实际使用场景中,有一些情景的交互要求,是记录用户的浏览状态的。最常见的就是在列表页进入详情页之后,再返回到列表页,用户希望返回到进入详情页之前的状态继续操作。但是有些使用场景,用户又是希望能够获取最新的数据,例如同级列表页之间切换的时候。

如此,针对上述两种使用场景,需要实现按需读取页面缓存。由于SPA应用的路由逻辑也是在前端实现的,因此可以在前端对路由的逻辑进行设置以实现所需效果。

使用技术

  • Vue.js作为主要框架
  • Vue-router作为前端路由管理器
  • Vuex作为状态管理工具

总体思路

keep-alive判断当前组件是否读取缓存的节点,在整个生命周期里面非常靠后,在afterEach之后,基本在组件实例创建之前。(因此在此之前对当前组件是否读取缓存进行处理都是可行的,我选择在全局前置守卫进行处理)

而判断当前组件是否缓存的节点,则早于组件的beforeRouteLeave钩子。

基于上述逻辑,本方案解决的逻辑是,对当前打开的页面进行判断,动态生成需要keepAlive的组件数组配置,对有可能需要缓存的先行进行缓存,然后在每次路由切换的时候,再进行判断,按需读取页面缓存。

  1. 使用kepp-alive进行缓存,使用include属性对需要缓存的页面进行配置。
  2. 由于需要缓存的页面配置系动态生成,所以使用vuex储存该配置。
  3. 在路由元信息中写入两个配置,一是该路由是否需要缓存,二是从相关路由进入时才进行缓存的特定路由数组。
  4. 在beforeEach进行设置,每次进入路由之前,对进入的路由及其所有父级路由进行判断,若需要缓存且命中特定路由数组,则将相关路由添加至缓存配置文件中;若不符合,则将相关路由删除。(此步骤实现了路由切换时,需要则读取缓存,不需要则重新获取数据。)
  5. 使用全局mixin,进入相关组件之前,对当前路由进行判断,如果需要缓存的则将该路由添加至缓存配置中。(此步骤实现了缓存当前打开的需要缓存的页面。)

具体实现

1. 使用include属性控制路由缓存

此处需要注意的是,include匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。

但是vue-router的环境下,是没有局部注册名称的,只能为组件补全name属性。

因此,请务必给组件添加 name 选项,否则匿名组件将全部应用缓存。

<keep-alive :include="$store.state.cachedRouteNames">
 <router-view />
</keep-alive>

2. 添加全局路由缓存配置

// store/index.js

const store = new vuex.Store({
 state: {
 // 缓存的路由列表
 cachedRouteNames: [],
 },
 mutations: {
 UPDATE_CACHEDROUTENAMES(state,{ action, route }) {
  const methods = {
  'add': () => {
  state.cachedRouteNames.push(route)
  },
  'delete': () => {
  state.cachedRouteNames.splice(state.cachedRouteNames.findIndex((e) => { return e === route}),1)
  }
  }
  methods[action]()
 }
 }
})

3. 配置路由元信息,对需要缓存的路由进行配置

keepAlive表明路由需要被缓存,必须,否则不缓存

cacheWhenFromRoutes为数组,非必须,若为falsy值,则任何时候均缓存;若为空数组,则任何时候均不缓存

// router/index.js

{
 path: '/productslist',
 name: 'ProductsList',
 component: ProductsList,
 meta: {
 keepAlive: true,
 cacheWhenFromRoutes: ['ProductDetail'] // 此处配置的是路由的name
 }
},

4. 配置全局前置守卫,按需读取缓存

// routeControl.js

// 需要缓存的路由名称数组
const cachedRouteNames = store.state.cachedRouteNames;

// 定义添加缓存组件name函数,设置的是组件的name
const addRoutes = (route) => {
 const routeName = route.components.default.name
 if (routeName && cachedRouteNames.indexOf(routeName) === -1) {
 store.commit('UPDATE_CACHEDROUTENAMES', { action: 'add', route: routeName })
 }
}

// 定义删除缓存组件name函数,设置的是组件的name
const deleteRoutes = (route) => {
 const routeName = route.components.default.name
 if (routeName && cachedRouteNames.indexOf(routeName) !== -1) {
 store.commit('UPDATE_CACHEDROUTENAMES', { action: 'delete', route: routeName })
 }
}

router.beforeEach((to, from, next) => {
 
 // 处理缓存路由开始
 // 在读取缓存之前,先对该组件是否读取缓存进行处理
 to.matched.forEach((item, index) => {
 const routes = item.meta.cacheWhenFromRoutes;
 /**
  * 此处有几种情况
  * 1. 没有配置cacheWhenFromRoutes, 则一直缓存;
  * 2. 配置了cacheWhenFromRoutes,但是首次打开此web app,则from.name为空,此时应该将该页面组件的name添加到缓存配置文件中
  * 3. 配置了cacheWhenFromRoutes,from.name不为空,若命中cacheWhenFromRoutes,则添加该页面组件的name到缓存配置文件中,否则删除。
  *
  **/
 if (item.meta.keepAlive && (!routes || (routes && (!from.name || routes.indexOf(from.name) !== -1)))) {
  addRoutes(item)
 } else {
  deleteRoutes(item)
 }
 
 })
 // 处理缓存路由结束

 new Promise(( resolve, reject ) => {
 // ..other codes
 }).then( res => {
 if ( res ) {
  next(res)
 } else {
  next()
 }
 })
})

// 全局混入。此步骤的目的是在该组件被解析之后,若是属于需要缓存的组件,先将其添加到缓存配置中,进行缓存。

// 导航守卫的最后一个步骤就是调用 beforeRouteEnter 守卫中传给 next 的回调函数,此时整个组件已经被解析,DOM也已经更新。

Vue.mixin({
 beforeRouteEnter(to, from, next) {
 next(vm => {
  to.matched.forEach((item) => {
  const routeName = item.components.default.name
  if (to.meta.keepAlive && routeName && cachedRouteNames.indexOf(routeName) === -1) {
   store.commit('UPDATE_CACHEDROUTENAMES', { action: 'add', route: routeName })
  }
  })
 })
 },
})

写在最后

坑点

  • 此方案涉及两个name,一个是设置特定路由时,使用路由的name。另一个是动态生成缓存配置文件时,使用的是页面组件的name。
  • 务必给组件添加name属性,便于include属性的使用,也方便调试跟踪。如果组件缺少name属性,将会默认使用缓存。
  • 动态处理缓存配置时,一定要对to.matched进行遍历,否则嵌套路由的父级路由的缓存就无法生效,将导致子路由的缓存也无法生效。
  • 全局混入有一定危险性,慎用...

以上是实践过程中摸索出来的一种解决方案,我相信存在更加优雅高效的解决方式。如果你正好实践过相关方法,烦请指正,谢谢。

更多参考

github.com/vuejs/vue/i…

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript最常用与实用的创建类的代码
Aug 12 Javascript
基于jquery实现的移入页面上空文本框时,让它变为焦点,移出清除焦点
Jul 26 Javascript
原生Javascript封装的一个AJAX函数分享
Oct 11 Javascript
JS组件Bootstrap Table表格多行拖拽效果实现代码
Dec 08 Javascript
jQuery 获取屏幕高度、宽度的简单实现案例
May 17 Javascript
Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )
Mar 31 Javascript
bootstrap3使用bootstrap datetimepicker日期插件
May 24 Javascript
详解基于DllPlugin和DllReferencePlugin的webpack构建优化
Jun 28 Javascript
基于D3.js实现时钟效果
Jul 17 Javascript
vue中使用sessionStorage记住密码功能
Jul 24 Javascript
关于JS解构的5种有趣用法
Sep 05 Javascript
js实现动态时钟
Mar 12 Javascript
JavaScript执行环境及作用域链实例分析
Aug 01 #Javascript
Vue.js 利用v-for中的index值实现隔行变色
Aug 01 #Javascript
echarts设置图例颜色和地图底色的方法实例
Aug 01 #Javascript
看看“疫苗查询”小程序有温度的代码
Jul 31 #Javascript
Vue父子组件双向绑定传值的实现方法
Jul 31 #Javascript
react中实现搜索结果中关键词高亮显示
Jul 31 #Javascript
vue2.0页面前进刷新回退不刷新的实现方法
Jul 31 #Javascript
You might like
Apache 配置详解(最好的APACHE配置教程)
2010/07/04 PHP
php中使用parse_url()对网址进行解析的实现代码(parse_url详解)
2012/01/03 PHP
PHP读取xml方法介绍
2013/01/12 PHP
PHP基于单例模式实现的mysql类
2016/01/09 PHP
thinkPHP多域名情况下使用memcache方式共享session数据的实现方法
2016/07/21 PHP
Laravel5中防止XSS跨站攻击的方法
2016/10/10 PHP
php中目录操作opendir()、readdir()及scandir()用法示例
2019/06/08 PHP
myFocus slide3D v1.1.0 使用方法与下载
2011/01/12 Javascript
jquery对复选框(checkbox)的操作汇总
2016/01/13 Javascript
值得分享的轻量级Bootstrap Table表格插件
2016/05/30 Javascript
jQuery中的ready函数与window.onload谁先执行
2016/06/21 Javascript
jQuery实现返回顶部按钮和scroll滚动功能[带动画效果]
2017/07/05 jQuery
详解Vue学习笔记入门篇之组件的内容分发(slot)
2017/07/17 Javascript
[08:44]和酒神一起战斗 DOTA2教你做大人
2014/03/27 DOTA
python中的__slots__使用示例
2015/02/26 Python
《Python之禅》中对于Python编程过程中的一些建议
2015/04/03 Python
python 监听salt job状态,并任务数据推送到redis中的方法
2019/01/14 Python
python感知机实现代码
2019/01/18 Python
Python Web程序搭建简单的Web服务器
2019/07/31 Python
通过 Django Pagination 实现简单分页功能
2019/11/11 Python
Python数据可视化:顶级绘图库plotly详解
2019/12/07 Python
利用python读取YUV文件 转RGB 8bit/10bit通用
2019/12/09 Python
python BeautifulSoup库的安装与使用
2020/12/17 Python
Belle Maison倍美丛官网:日本千趣会旗下邮购网站
2016/07/22 全球购物
TUMI新加坡官网:国际领先的商旅箱包品牌
2019/01/12 全球购物
国际奢侈品品牌童装购物网站:Designer Childrenswear
2019/05/08 全球购物
DOUGLAS荷兰:购买香水和化妆品
2020/10/24 全球购物
一家外企的面试题目(C/C++面试题,C语言面试题)
2014/03/24 面试题
安全责任书
2015/01/29 职场文书
爱护环境卫生倡议书
2015/04/29 职场文书
2015财务年度工作总结范文
2015/05/04 职场文书
医生行业员工的辞职信
2019/06/24 职场文书
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
2021/04/17 Vue.js
Go语言切片前或中间插入项与内置copy()函数详解
2021/04/27 Golang
html5实现点击弹出图片功能
2021/07/16 HTML / CSS
关于mysql中时间日期类型和字符串类型的选择
2021/11/27 MySQL