引言

前端webapp应用为了追求类似于native模式的细致体验,总是在不断的在向native的体验靠拢;比如本文即将要说到的功能,native由于是多页应用,新页面可以启用一个的新的webview来打开,后退其实是关闭当前webview,其上一个webview就自然显示出来;但是在单页的webapp应用中,所有内容其实是在一个页面中展示的,不存在多页的情况,这时就需要前端开发来想办法实现相应的体验效果。

首先需要说明一下,本文所说的前进刷新后退不刷新是指组件是否重新渲染,比如列表A页面,点击其中的每一项进入详情B页面,然后从B页面后退到列表A页面时,A页面没有重新渲染,也没有重新发送ajax请求。下面,我们就来说说在vue的单页应用中,实现前进刷新后退不刷新的一些实现方案,其他的方案大家可以一起补充。

keep-alive方案

keep-alive是vue官方提供的一种缓存组件实例的方法,vue官网对其用法的介绍:

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

正如vue官网的介绍,我们在开发中就可以使用他这一点来缓存后退不用刷新的路由组件。具体的实现思路如下。

1、模板中使用keep-alive来缓存对应的路由组件

在app.vue模板中改写<router-view>,具体可以这样:

  1. <keep-alive>
  2. <router-view v-if="$route.meta.keepAlive">
  3. <!-- 这里是会被缓存的视图组件,比如列表A页面 -->
  4. </router-view>
  5. </keep-alive>
  6. <router-view v-if="!$route.meta.keepAlive">
  7. <!-- 这里是不被缓存的视图组件,比如详情B页面-->
  8. </router-view>

这种方式需要通过vue路由元信息的配合,当然也可以像下面这样:

  1. <keep-alive include="A">
  2. <router-view>
  3. <!-- 只有路径匹配到的视图组件,如上面的列表A页面会被缓存! -->
  4. </router-view>
  5. </keep-alive>

这种方式缺点是:

  1. 需要事先知道路由组件的**name**值,这在大型项目中不是一个特别好的选择。

2、在路由配置文件中配置路由元信息

下面以第一种模板方式来展开介绍。对应上面模板文件中的路由元数据配置如下:

  1. routes: [{
  2. path: '/',
  3. name: 'home',
  4. component: Home,
  5. meta: {
  6. keepAlive: false //此组件不需要被缓存
  7. }
  8. },
  9. {
  10. path: '/list',
  11. name: 'list',
  12. component: List,
  13. meta: {
  14. keepAlive: true //此组件需要被缓存
  15. }
  16. },
  17. {
  18. path: '/detail',
  19. name: 'detail',
  20. component: Detail,
  21. meta: {
  22. keepAlive: false // 此组件需要被缓存
  23. }
  24. }
  25. ]

3、在keep-alive组件提供activated钩子函数实现数据更新逻辑

需要强调的是keep-alive组件(这里是指keep-alive包裹的路由组件,下同)与一个vue组件是有区别的,vue的具体生命周期函数可以参考这里;而keep-alive组件,除了正常vue组件提供的生命周期之外,其额外新增了2个跟keep-alive相关的钩子函数:

  • activated: 缓存的组件再次进入时会触发
  • deactivated: 缓存的组件离开时会触发

既然keep-alive组件提供了这么多生命周期函数钩子,那么这些钩子函数具体的执行顺序是怎样的呢?

第一次进入keep-alive组件时,其生命周期执行顺序:

  1. beforeRouteEnter --> created --> mounted --> activated --> deactivated

非首次进入时,其生命周期执行顺序:

  1. beforeRouteEnter -->activated --> deactivated

可以看到,非首次进入keep-alive组件时,正常的vue组件生命周期函数是不会在执行,而会执行keep-alive新增的两个周期钩子函数。同时也可以看出离开keep-alive组件时其destroy周期函数并没有执行,从侧面证明缓存组件并没有销毁。根据介绍,我们可以:

通过利用keep-alive提供activated钩子函数来决定是否进行ajax请求来更新组件,以及deactivated钩子函数来重置页面相关状态。

keep-alive实现后推不刷新的方案,有一些地方需要特别注意:

  • keep-alive组件的更新时机要有清晰的认知

意思就是在开发过程中需要知道后退不刷新组件虽然不重新渲染,但是要知道组件数据在什么情况下需要重新发送ajax请求来获取数据,从而更新组件。

就拿上面的A、B页面来说,我们需要知道列表A页面对应的keep-alive组件在什么时候进行更新,因为进入A页面的入口可以是从B页面后退而来,也可能从其他页面前进而来;当然需要对这两种不同情况需要加以区分,否则A页面的数据就一直是第一次缓存过的数据。

这篇文章给出了一种解决方案:

首先,在每个路由元信息meta中添加一个isBack字段,用来解决beforeRouterEnter不能直接访问vue实例。

  1. ...
  2. {
  3. path: '/list',
  4. name: 'list',
  5. component: List,
  6. meta: {
  7. keepAlive: true, //此组件需要被缓存
  8. isBack: false
  9. }
  10. }
  11. ...

然后,借助beforeRouteEnter钩子函数来判断页面来源:

  1. beforeRouteEnter(to, from, next) {
  2. if(from.name === 'detail') { //判断是从哪个路由过来的,若是detail页面不需要刷新获取新数据,直接用之前缓存的数据即可
  3. to.meta.isBack = true;
  4. }
  5. next();
  6. },

最后,需要借助keep-alive提供钩子函数activated来完成是否更新:

  1. activated() {
  2. if(!this.$route.meta.isBack) {
  3. // 如果isBack是false,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
  4. this.getData(); // ajax获取数据方法
  5. }
  6. // 恢复成默认的false,避免isBack一直是true,导致下次无法获取数据
  7. this.$route.meta.isBack = false
  8. },
  • keep-alive组件前进的页面刷新导致keep-alive组件状态丢失

继续以上面的A、B页面为例,在进入详情B页面后,然后刷新,这时列表A页面的缓存的数据都丢失了,由于上面的判断规则也会导致不会重新获取数据。所以对于这种问题,还需要额外加一些判断条件。由于keep-alive第一次进入时会执行created方法,所以利用这点加一个标识来加以判断:

  1. //第一次进入keep-alive路由组件时
  2. created() {
  3. this.isFirstEnter = true;
  4. // 只有第一次进入或者刷新页面后才会执行此钩子函数,使用keep-alive后(2+次)进入不会再执行此钩子函数
  5. },

activated钩子函数也需要增加对应的判断:

  1. activated() {
  2. if(!this.$route.meta.isBack || this.isFirstEnter){
  3. // 如果isBack是false,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
  4. // 如果isFirstEnter是true,表明是第一次进入此页面或用户刷新了页面,需获取新数据
  5. this.data = ''// 把数据清空,可以稍微避免让用户看到之前缓存的数据
  6. this.getData();
  7. }
  8. // 恢复成默认的false,避免isBack一直是true,导致下次无法获取数据
  9. this.$route.meta.isBack=false
  10. // 恢复成默认的false,避免isBack一直是true,导致每次都获取新数据
  11. this.isFirstEnter=false;
  12. },
  • 缓存过多keep-alive组件,因常驻内存会导致内存占用过多

这是一个特别需要注意的问题,尤其是当整个系统或者系统大部分页面都使用keep-alive来缓存组件时,由于其是缓存在内存中的,若不加处理,内存堆积越来越大,导致系统卡顿。正确的解决方案是:需要及时销毁掉内存缓存的组件

具体可以参考:vue issue#6509记一次vue 的keep-alive踩坑之路两篇文章的实现思路。

嵌套路由

嵌套路由具体的实现可以参考官网,这种方案也是一种解决思路。下面以一个具体的例子(如下图所示)来说一下实现的具体过程。

正如上图所示,一个下单页面有6处跳出当前页面查看规则、协议或者修改具体某些内容的页面,因为这6项依赖这个订单页,那么可以使用路由嵌套来实现这种后退不刷新的过程,下单页作为父路由,其他跳转项可以作为其子路由。具体步骤:

1、配置路由信息

  1. {
  2. path: '/order',
  3. component: Order,
  4. children: [
  5. {
  6. path: 'invoice',
  7. component: Invoice
  8. }, {
  9. path: 'contact',
  10. component: Contact
  11. },
  12. {
  13. path: 'costrule',
  14. component: CostRule
  15. }, {
  16. path: 'refundrule',
  17. component: RefundRule
  18. },{
  19. path: 'useragreement',
  20. component: UserAgreement
  21. },{
  22. path: 'payrule',
  23. component: PayRule
  24. }
  25. ]
  26. }

2、在下单页Order组件模板中配置路由嵌套。

  1. <div class="safe-area-pb">
  2. <purchase />
  3. <router-view />
  4. </div>

这样,通过下单页进入其他页面比如进入修改联系人信息页面,那么路由从/order进入到/order/contact,修改完成后回退会回到父路由/order中,完成后推不刷新的功能。

当然,正如上面所说的,嵌套路由方案只是一种可选择方案,有其对应的使用场景;另外,使用过程还需要注意以下几点:

**1、进入子路由后,若是在子路由强制刷新后,父子路由的组件都会重新渲染,执行各自路由组件的生命周期;父路由中设置相关逻辑都会执行。

**2、子路由若被其他页面共用,这时进入子路由时会触发第一点的情况,所以最好子路由是父路由独占的。

component组件配合路由方案

这种方案主要是利用vue提供的动态路由组件component来实现,页面组件的切换不再根据路由path来决定,而是根据不同的业务逻辑加载不同的动态组件。具体的实现可以参考这篇文章解决方案第6点部分:异步加载的业务线如何动态注册路由?。同样,同步路由也可以使用动态路由来完成对应后退不刷新功能。这不过这种方式的使用场景更急局限。

总结

上面提供的3种解决方案,第一种方案大家都比较熟悉,后面两种可能相对来说就比较陌生。它们只是解决同一问题的不同解决方案,想必还有其他的解决方案本人没有想到,有其他更好方案的可以一起探讨。

参考

1、滴滴 webapp 5.0 Vue 2.0 重构经验分享

2、另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新

3、vue-router 之 keep-alive

4、记一次vue 的keep-alive踩坑之路

5、希望keep-alive能增加可以动态删除已缓存组件的功能

vue单页应用前进刷新后退不刷新方案探讨的更多相关文章

  1. 另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新

    目的:vue-cli构建的vue单页面应用,某些特定的页面,实现前进刷新,后退不刷新,类似app般的用户体验.注: 此处的刷新特指当进入此页面时,触发ajax请求,向服务器获取数据.不刷新特指当进入此 ...

  2. 解决vue单页路由跳转后scrollTop的问题

    作为vue的初级使用者,在开发过程中遇到的坑太多了.在看页面的时候发现了页面滚动的问题,当一个页面滚动了,点击页面上的路由调到下一个页面时,跳转后的页面也是滚动的,滚动条并不是在页面的顶部 在我们写路 ...

  3. vue单页应用中 返回列表记住上次滚动位置、keep-alive缓存之后更新列表数据 那点事

    实践场景需求 产品列表中,滚动到一定位置的时候,点击查看产品信息,后退之后,需要回到原先的滚动位置,这是常见的需求 所有页面均在router-view中,暂时使用了keep-alive来缓存所有页面, ...

  4. 如何在vue单页应用中使用百度地图

    作为一名开发人员,每次接到开发任务,我们首先应该先分析需求,然后再思考技术方案和解决方案.三思而后行,这是一个好的习惯. 需求:本项目是采用vue组件化开发的单页应用项目,现需要在项目中引入百度的地图 ...

  5. 基于vue单页应用的例子

    代码地址如下:http://www.demodashi.com/demo/13374.html 目录结构 src目录 主要的代码目录 components 存放项目组件 router 路由文件 sto ...

  6. vue 单页应用中微信支付的坑

    vue 单页应用中微信支付的坑 标签(空格分隔): 微信 支付 坑 vue 场景 在微信H5页面(使用 vue-router2 控制路由的 vue2 单页应用项目)中使用微信 jssdk 进行微信支付 ...

  7. Vue 基于node npm & vue-cli & element UI创建vue单页应用

    基于node npm & vue-cli & element UI创建vue单页应用 开发环境   Win 10   node-v10.15.3-x64.msi 下载地址: https ...

  8. [转] 2017-11-20 发布 另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新

    目的:vue-cli构建的vue单页面应用,某些特定的页面,实现前进刷新,后退不刷新,类似app般的用户体验.注: 此处的刷新特指当进入此页面时,触发ajax请求,向服务器获取数据.不刷新特指当进入此 ...

  9. vue单页应用添加百度统计

    前言 申请百度统计后,会得到一段JS代码,需要插入到每个网页中去,在Vue.js项目首先想到的可能就是,把统计代码插入到index.html入口文件中,这样就全局插入,每个页面就都有了;这样做就涉及到 ...

随机推荐

  1. scrapy Mongodb 储存

    pipelines.py # -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your ...

  2. 安卓学习 Drawable对象

    whie(!images[currentImage].endWith(".PNG")&&!images[currentImage].endWith(".p ...

  3. docker使用代理(测试docker 17.06)

    环境:debian9 service docker stop sudo HTTP_PROXY=http://127.0.0.1:1080 dockerd sudo docker pull gcr.io ...

  4. LR回放https协议脚本失败:[GENERAL_MSG_CAT_SSL_ERROR]connect to host "XXX" failed:[10054] Connection reset by peer [MsgId:MERR-27780]

    Loadrunner默认发送是通过sockets(将http转换为sockets)发送的,而sockets默认SSL的版本为SSL2和SSL3.HTTPS协议录制的脚本以SSL3版本回放时会使sock ...

  5. Texture转Texture2D

    private Texture2D TextureToTexture2D(Texture texture) { Texture2D texture2D = new Texture2D(texture. ...

  6. Vue.js 入门教程

    Vue.js 入门教程:https://cn.vuejs.org/v2/guide/index.html

  7. UI行业发展预测 & 系列规划的调整

    又双叒叕拖更了,上一篇还是1月22号更新的,这都3月9号了…… 前面几期把职业规划.能力分析.几个分析用的设计理论都写完了,当然实际工作中用到的方法论不止上面这些,后续会接着学习: 如果你的目标是一线 ...

  8. 为什么禁止在 foreach 循环里进行元素的 remove/add 操作

    首先看下边一个例子,展示了正确的做法和错误的错发: 这是为什么呢,具体原因下面进行详细说明: 1.foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数 ...

  9. easyui-combobox的option选项为true与false时的问题

    如题,我们使用easyui-combobox,当我们的选择项为true与false时,即选择是否,后台返回一个boolean类型的变量,那么这时候,通过form表单进行反显会出现这样的问题:表单里ea ...

  10. python3 tkinter添加图片和文本

    在前面一篇文章基础上,使用tkinter添加图片和文本.在开始之前,我们需要安装Pillow图片库. 一.Pillow的安装 1.方法一:需要下载exe文件,根据下面图片下载和安装       下载完 ...