vue单页应用前进刷新后退不刷新方案探讨

引言

前端webapp应用为了追求相似于native模式的细致体验,老是在不断的在向native的体验靠拢;好比本文即将要说到的功能,native因为是多页应用,新页面能够启用一个的新的webview来打开,后退实际上是关闭当前webview,其上一个webview就天然显示出来;可是在单页的webapp应用中,全部内容实际上是在一个页面中展现的,不存在多页的状况,这时就须要前端开发来想办法实现相应的体验效果。javascript

首先须要说明一下,本文所说的前进刷新后退不刷新是指组件是否从新渲染,好比列表A页面,点击其中的每一项进入详情B页面,而后从B页面后退到列表A页面时,A页面没有从新渲染,也没有从新发送ajax请求。下面,咱们就来讲说在vue的单页应用中,实现前进刷新后退不刷新的一些实现方案,其余的方案你们能够一块儿补充。html

keep-alive方案

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

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

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

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

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

<keep-alive>
    <router-view v-if="$route.meta.keepAlive">
        <!-- 这里是会被缓存的视图组件,好比列表A页面 -->
    </router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive">
    <!-- 这里是不被缓存的视图组件,好比详情B页面-->
</router-view>

这种方式须要经过vue路由元信息的配合,固然也能够像下面这样:github

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

这种方式缺点是:web

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

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

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

routes: [{
        path: '/',
        name: 'home',
        component: Home,
        meta: {
            keepAlive: false //此组件不须要被缓存
        }
    },
    {
        path: '/list',
        name: 'list',
        component: List,
        meta: {
            keepAlive: true //此组件须要被缓存
        }
    },
    {
        path: '/detail',
        name: 'detail',
        component: Detail,
        meta: {
            keepAlive: false // 此组件须要被缓存
        }
    }
]

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

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

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

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

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

beforeRouteEnter --> created --> mounted --> activated --> deactivated

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

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实例。

...
    {
        path: '/list',
        name: 'list',
        component: List,
        meta: {
            keepAlive: true, //此组件须要被缓存
            isBack: false
        }
    }
    ...

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

beforeRouteEnter(to, from, next) {
      if(from.name === 'detail') { //判断是从哪一个路由过来的,如果detail页面不须要刷新获取新数据,直接用以前缓存的数据便可
          to.meta.isBack = true;
      }
      next();
    },

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

activated() {
    if(!this.$route.meta.isBack) {
      // 若是isBack是false,代表须要获取新数据,不然就再也不请求,直接使用缓存的数据
      this.getData(); // ajax获取数据方法
    }
    // 恢复成默认的false,避免isBack一直是true,致使下次没法获取数据
    this.$route.meta.isBack = false
  },
  • keep-alive组件前进的页面刷新致使keep-alive组件状态丢失

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

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

activated钩子函数也须要增长对应的判断:

activated() {
     if(!this.$route.meta.isBack || this.isFirstEnter){
         // 若是isBack是false,代表须要获取新数据,不然就再也不请求,直接使用缓存的数据
         // 若是isFirstEnter是true,代表是第一次进入此页面或用户刷新了页面,需获取新数据
         this.data = ''// 把数据清空,能够稍微避免让用户看到以前缓存的数据
         this.getData();
     }
     // 恢复成默认的false,避免isBack一直是true,致使下次没法获取数据
     this.$route.meta.isBack=false
     // 恢复成默认的false,避免isBack一直是true,致使每次都获取新数据
     this.isFirstEnter=false;
   },
  • 缓存过多keep-alive组件,因常驻内存会致使内存占用过多

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

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

嵌套路由

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

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

一、配置路由信息

{
      path: '/order',
      component: Order,
      children: [
        {
          path: 'invoice',
          component: Invoice
        }, {
          path: 'contact',
          component: Contact
        },
        {
          path: 'costrule',
          component: CostRule
        }, {
          path: 'refundrule',
          component: RefundRule
        },{
          path: 'useragreement',
          component: UserAgreement
        },{
          path: 'payrule',
          component: PayRule
        }
      ]
    }

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

<div class="safe-area-pb">
     <purchase />
     <router-view />    
    </div>

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

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

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

**二、子路由若被其余页面共用,这时进入子路由时会触发第一点的状况,因此最好子路由是父路由独占的。

component组件配合路由方案

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

总结

上面提供的3种解决方案,第一种方案你们都比较熟悉,后面两种可能相对来讲就比较陌生。它们只是解决同一问题的不一样解决方案,想必还有其余的解决方案本人没有想到,有其余更好方案的能够一块儿探讨。

参考

一、滴滴 webapp 5.0 Vue 2.0 重构经验分享
二、另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
三、vue-router 之 keep-alive
四、记一次vue 的keep-alive踩坑之路
五、但愿keep-alive能增长能够动态删除已缓存组件的功能

相关文章
相关标签/搜索