在构建SPA应用时,经常遇到的场景就是列表页面 跳转到详情页,但是详情页面回退到列表页面,列表页面有重新刷新了。
如何保证回退的时候不刷新页面呢?keep-alive是一个非常好的解决方案(当然你也可以以子路由,以绝对定位形势覆盖上去*_*)。
keep-alive的作用
keep-alive是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
本篇主要讲keep-alive的用法。
keep-alive用法
像Tabs加载动态组件(有的童鞋直接用动态组件取代路由功能——个人不推荐)
缓存组件
<keep-alive> <component include="a" exclude="b"> <!-- name 为 a 的组件将被缓存!,name 为 b 的组件不被缓存! --> </component> </keep-alive>
缓存路由
<keep-alive include="a"> <router-view> <!-- 只有路径匹配到的视图 a 组件会被缓存! --> </router-view> </keep-alive>
推荐用法:不需要例举出需要被缓存组件名称
<keep-alive> <router-view v-if="$route.meta.keepAlive"> <!-- 这里是会被缓存的视图组件,比如 Home! --> </router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive"> <!-- 这里是不被缓存的视图组件,比如 Edit! --> </router-view>
其实就是用keep-alive包括原来的组件即可,包括的组件并不像普通组件的那样,slot。keep-alive只是一个抽象组件。具体看下篇的《vue keep-alive(2):剖析keep-alive的实现原理—学习笔记整理》。
keep-alive 的props
-
include - 字符串或正则表达,只有匹配的组件会被缓存
-
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
-
max- 缓存组件的最大个数,超出上限使用LRU的策略置换缓存数据。
keep-alive+vue组件声明周期+路由守卫
keep-alive的作用是在内存中缓存组件(不让组件销毁),等到下次再渲染的时候,还会保持其中的所有状态。
注意:实际保存在内存中的不是渲染后的HTML的节点字符串,而是vue编译后虚拟化的DOM对象。
目的是防止重复渲染DOM,当数据发生了变化时,才会促发VM的diff更新。
vue所有功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和DOM渲染两大重要功能。
keep-alive缓存了组件,但是它也阻挡了vue 正常的生命周期流程。
vue的生命周期有:beforeCreate,created,beforeMonted,mounted,beforUpdate,update,beforDestroy,destroyed,详情参看《vue2.x入坑总结—回顾对比angularJS/React》
当在不同页面/组件之间切换的时候都会请求一些请求过的数据,每次请求都会导致重复渲染影响性能。这些数据可以存到缓存中——此时使用keep-alive将组建包裹起来,但这样以上八种生命周期钩子将失效。
取而代之的是activate和deactivated,在 2.2.0 及其更高版本中,activated 和 deactivated 将会在 树内的所有嵌套组件中触发。
-
activate:是在被包裹组建被激活的状态下使用的生命周期钩子(每次组件被调用时都会执行activated——包括第一次)。
-
第一次进入缓存路由/组件:beforeRouteEnter > beforeCreate > created > mounted > activated > ... ... > beforeRouteLeave > deactivated
-
再次进入组件时:beforeRouteEnter > activated > ... ... > beforeRouteLeave > deactivated
-
-
deactivated:在被包裹组件停止使用时调用
理论上:当在项目中引入keep-alive的时候,
-
页面第一次进入,钩子函数的触发顺序created -> mounted -> activated,
-
退出时触发deactivated。
-
再次进入(前进或者后退)时,只触发activated。如果created或mounted中处理刷新页面,这个钩子没有被调用,无法刷新。
beforeRouteEnter 总会触发
-
不使用 keep-alive 时,
beforeRouteEnter --> created --> mounted --> destroyed
-
使用 keep-alive 时,
beforeRouteEnter --> created --> mounted --> activated --> deactivated
更多的坑,推荐看
《Vue生命周期全解(vueRouter,keep-alive,生命周期)》
《Vue 爬坑之旅 -- keepAlive 与 vue-router 结合使用实现页面缓存及记住滚动位置功能的实现及几个需要注意的点》
keep-alive activate 在项目中应用
一般不只是点到详情页,然后直接返回怎么简单
状态刷新
比如你在详情页面添加到了购物车,列表页面需要展示购车数量等,需要结合vuex实现。
更多的是,每次进入这个页面,必须请求后台一些数据,就需要在activate里面实现
有发现有个同事的实现是:页面离开,settimeout,臆测一个时间去通知列表页面,去进行操作。这个,嗯,能行,但是绝对不靠谱!
滚动条复位
可以通过:vue-keep-scroll-position 组件解决。但是这个组件,只有刷新路由的容器。如果是页面里面一个列表需要刷新呢?
在需要缓存的页面,如列表页面的beforeRouteLeave,存储滚动条位置信息。其他操作原理类同。
// 离开时 beforeRouteLeave(to: any, from: any, next: any) { //let dom: HTMLElement|null = document.getElementById('list'); let dom = this.$refs.list; // 离开时,想办法存储滚动位置。 to.meta.scrollTop = dom.scrollTop; } // 返回时 activated() { // 激活后,更加存储的信息,重新设置滚动条 const scrollTop = this.$route.meta.scrollTop; if (scrollTop) { this.$nextTick(() => { this.$refs.list.scrollTop = scrollTop; }); } }
这里需要注意的是,不要通过监听scroll事件,在列表页面实时刷新滚动条位置信息——没有必要的性能消耗。
keep-alive缓存页面限制
仅当从B页面回退到A页面,恢复A页面之前的缓存转态。说个常见的案例吧
从面板页面跳转到编辑页面,需要缓存面板页面。即:从编辑页面回退到面板页面,面板页面还是之前的那个骚年(进入前的样子)。
面板页面
export default [ { path: '/panel/:biz_id', name: 'panel', component: () => import(/* webpackChunkName: "panel-page-chunk" */'@/pages/panel/index.vue'), children: [ { path: 'list_panel/:os_id', name: 'listPanel', component: () => import(/* webpackPrefetch: true */ /* webpackChunkName: "panel-page-chunk" */'@/pages/panel/list.vue'), props: route => ({ osId: route.params.os_id, }), meta: { keepAlive: true, }, }, { path: 'panel-info', name: 'panel-info', component: () => import(/* webpackPrefetch: true */ /* webpackChunkName: "panel-page-chunk" */'@/pages/panel/info.vue'), }, ], }, { path: '/panel/:biz_id/chart_editor/:os_id/:chart_id?', name: 'chartEditor', component: () => import(/* webpackPrefetch: true */ /* webpackChunkName: "panel-page-chunk" */'@/pages/panel/chartEditor.vue'), meta: { keepAlive: false, }, props: route => ({ osId: route.params.os_id, }), }, ];
需要在面板页面里面,beforeRouteLeave 设置,from.meta.keepAlive = false;
在编辑页面,beforeRouteLeave设置,to.meta.keepAlive = true;
这个其实是一般方案,如《缓存之keep-alive的理解和应用》,个人觉得这个方案比较好《vue组件缓存之keep-alive正确使用姿势》,其实就是在路由的meta设置 路径的权重。如一级页面权重100,二级页面1000,三级页面1000。根据权重去做页面缓存与 页面切换动画的滑动方向。
keep-alive需要注意的地方总结
-
先匹配被包含组件的 name 字段,如果 name 不可用,则匹配当前组件 components 配置中的注册名称。
-
不会在函数式组件中正常工作,因为它们没有缓存实例。
-
当匹配条件同时在 include 与 exclude 存在时,以 exclude 优先级最高(当前vue 2.4.2 version)。比如:包含于排除同时匹配到了组件A,那组件A不会被缓存。
-
包含在include中,但符合 exclude ,不会调用activated 和 deactivated。
参考文章:
vue学习---生命周期钩子activated,deactivated https://www.cnblogs.com/mengtong/p/10966955.html
vue 页面回退mounted函数不执行的问题及解决方法 https://www.cnblogs.com/candy-xia/p/11425357.html
深入理解vue-router之keep-alive https://www.jb51.net/article/122570.htm
官方:<keep-alive>组件缓存问题 #811 https://github.com/vuejs/vue-router/issues/811#issuecomment-353875880
转载本站文章《vue keep-alive(1):vue router如何保证页面回退页面不刷新?》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue/8236.html