资讯专栏INFORMATION COLUMN

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

Ocean / 3343人阅读

摘要:如何添加这个条件,判断用户是否刷新了页面呢我们知道,当使用后,只有第一次进入后会触发钩子函数,再次进入就不再执行了。

目的:vue-cli构建的vue单页面应用,某些特定的页面,实现前进刷新,后退不刷新,类似app般的用户体验。
注: 此处的刷新特指当进入此页面时,触发ajax请求,向服务器获取数据。不刷新特指当进入此页面时,不触发ajax请求,而是使用之前缓存的数据,以便减少服务器请求,用户体验更流畅。

项目需求:

任何技术的探索,都来自项目的需求。之前经手的一个项目是微信端商城,使用的是传统的mvc模式,利用的是jq+js,因此对于商城的项目需求比较熟悉。目前在学习vue,练手一个商城,遇到之前经常提及而无法很好解决的需求。有些页面需要前进刷新,后退不刷新。比如,从商城的【首页】-->【详情页】-->【订单提交页】,每次打开新页面都需要获取新数据,但是按下返回键后,就不需要再获取新数据了,而滚动条还保留在之前的位置。最常见的操作是从【首页】-->【详情页】,然后在从【详情页】-->【首页】,如此反复。
实例如图:

前人经验:

前人栽树,后人好乘凉。技术圈的分享一直都在蓬勃发展。遇到问题,我们可以尽情去搜索,去寻找大佬的足迹。针对上述需求,看到一个分享vue-router 之 keep-alive,比较符合我的需求,但是使用到我的项目上发现,稍微有点不适合。此分享技术要点,比较适合两个页面之前的跳转,返回。而我的页面是多个路由(2+)之间的跳转,返回。无奈,只能去自己探索发现。不过此技术要点给了我很好的启发,特此感谢作者。@ RoamIn

实现思路:

注:demo中,index页面包含三个链接导航。page1-->page2-->page3.依次前进,每次前进到一个新页面都需要获取数据,而按下后退键后,从page3返回到page2,page2不再获取新数据,而是使用之前缓存的数据。从page2返回到page1时,page1不再获取新数据,而是使用之前的数据。所以,page1和page2需要缓存,page3不需要缓存。可以把page1想象成首页,page2想象成详情页,page3想象成订单提交页。这样方便理解。

利用keep-alive 缓存需要缓存的页面

在app.vue中改写router-view


    
        
    



    

在router/index.js中添加路由元信息,设置需要缓存的页面

routes: [{
        path: "/",
        name: "index",
        component: index,
        meta: {
            keepAlive: false, //此组件不需要被缓存
        }
    },
    {
        path: "/page1",
        name: "page1",
        component: page1,
        meta: {
            keepAlive: true, //此组件需要被缓存
            
        }
    },
    {
        path: "/page2",
        name: "page2",
        component: page2,
        meta: {
            keepAlive: true, // 此组件需要被缓存
           
        }
    },
    {
        path: "/page3",
        name: "page3",
        component: page3,
        meta: {
            keepAlive: false, // 此组件不需要被缓存
        }
    }
]

钩子函数的执行顺序

不使用keep-alive
beforeRouteEnter --> created --> mounted --> destroyed

使用keep-alive
beforeRouteEnter --> created --> mounted --> activated --> deactivated
再次进入缓存的页面,只会触发beforeRouteEnter -->activated --> deactivated 。created和mounted不会再执行。我们可以利用不同的钩子函数,做不同的事。务必理解上述钩子函数的执行时机和执行顺序,本教程的核心就依赖于此钩子函数
activated和deactivated是使用keep-alive后,vue中比较重要的两个钩子函数,建议详细了解下

需缓存的页面的写法

注:demo中的page1和page2,这两个页面都需要缓存,思路一样,以下以page1为例,page2不再赘述。
示例文件:components/page1.vue

data中初始化一个str字符串,存放从后台获取的数据

     data() {
       return {
         msg: "我是第一个页面",
         str: ""  // 加载页面后执行获取数据的方法,插入到此
       };
     }

methods中创建一个方法,模拟从后台获取数据

     methods: {
       getData() {
         // getData方法,模拟从后台请求数据
         this.str = "我是通过调用方法加载的数据。。。";
       }
     }

修改router/index.js中的配置

每次进入页面,我们都需要知晓是从哪个页面进来的,用以判断是否需要获取数据。以这个page1页面为例,当我们知晓是从page2过来的,我们就可以认为是用户操作了返回键,这时page1页面就不需要再获取新数据了,使用之前缓存的数据就可以了。如果是从别的页面过来的,我们就需要获取数据。

我们可以通过beforeRouteEnter这个钩子函数中的from参数判断是从哪个页面过来的,这个参数执行时,组件实例还没创建,所有不能在data中定义变量。我们可以在路由中定义一个变量,用来判断。

在router/index.js的meta中添加isBack变量,默认false

     {
          path: "/page1",
          name: "page1",
          component: page1,
          meta: {
              keepAlive: true, //此组件需要被缓存
              isBack:false, //用于判断上一个页面是哪个
          }
      },
      {
          path: "/page2",
          name: "page2",
          component: page2,
          meta: {
              keepAlive: true, // 此组件需要被缓存
              isBack:false, //用于判断上一个页面是哪个
          }
      },

beforeRouteEnter中判断是从哪个页面过来的

判断是从哪个路由过来的,如果是page2过来的,表明当前页面不需要刷新获取新数据,直接用之前缓存的数据即可

    beforeRouteEnter(to, from, next) {
      // 路由导航钩子,此时还不能获取组件实例 `this`,所以无法在data中定义变量(利用vm除外)
      // 参考 https://router.vuejs.org/zh-cn/advanced/navigation-guards.html
      // 所以,利用路由元信息中的meta字段设置变量,方便在各个位置获取。这就是为什么在meta中定义isBack
      // 参考 https://router.vuejs.org/zh-cn/advanced/meta.html
      if(from.name=="page2"){
          to.meta.isBack=true;
          //判断是从哪个路由过来的,
          //如果是page2过来的,表明当前页面不需要刷新获取新数据,直接用之前缓存的数据即可
      }
  
      next();
    },

activated中执行getData这个获取数据的方法

因为这个页面需要缓存。只有第一次进入时才会执行created和mounted方法,再次进入就不执行了。而activated每次进入都执行,所以在这个钩子函数中获取数据。

activated() {
  if(!this.$route.meta.isBack){
    // 如果isBack是false,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
    this.getData();
  }
  // 恢复成默认的false,避免isBack一直是true,导致下次无法获取数据
  this.$route.meta.isBack=false

},

这样就可以了?

当这样设置完毕后,你执行起来,貌似是可以了。第一次进入page1,能获取新数据,从page2返回时,不再获取新数据了,而是使用之前缓存的数据。但这样还有一个问题,当用户从page1进入page2后,因为某种原因,手动刷新了page2的页面。这时再返回到page1,发现之前缓存的数据丢失了,而且也没有再重新获取。所以我们还需要再添加一个判断条件,当用户手动刷新页面后,再返回时就需要重新获取数据了。

如何添加这个条件,判断用户是否刷新了页面呢?我们知道,当使用keep-alive后,只有第一次进入后会触发created钩子函数,再次进入就不再执行了。当用户刷新了页面,这个钩子函数就会又执行,所以,我们可以利用这个小技巧来做点文章。

data中定义变量isFirstEnter用来判断是否第一次进入,或是否刷新了页面,默认false

   data() {
     return {
       msg: "我是第一个页面",
       str: "",  // 加载页面后执行获取数据的方法,插入到此
       isFirstEnter:false // 是否第一次进入,默认false
     };
   },

created中把isFirstEnter变为true,说明是第一次进入或刷新了页面

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

activated中增加判断条件

   activated() {
     if(!this.$route.meta.isBack || this.isFirstEnter){
         // 如果isBack是false,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
         // 如果isFirstEnter是true,表明是第一次进入此页面或用户刷新了页面,需获取新数据
         this.str=""// 把数据清空,可以稍微避免让用户看到之前缓存的数据
         this.getData();
     }
     // 恢复成默认的false,避免isBack一直是true,导致下次无法获取数据
     this.$route.meta.isBack=false
     // 恢复成默认的false,避免isBack一直是true,导致每次都获取新数据
     this.isFirstEnter=false;
 
   },

这样应该就可以了

不需要缓存页面的写法

注:demo中的page3,这个页面不需要缓存,该怎么写就怎么写,不需要做特别的设置。

其它设置:

使用keep-alive后,可能有点小问题:第二个页面可能继承第一个页面的滚动条的高度。(在我项目中遇到的)
比如:page1向下滚动后,再进入page2,这时page2的滚动条可能是之前的高度,可能不会在顶部。

解决方法一
每次离开记录滚动条的高度,再次进入时根据项目需要再恢复之前的高度,或者置顶。

解决方法二(推荐)
router/index.js中添加如下代码(如不理解,请看参考链接)
参考:HTML5 History 模式     滚动行为

  mode: "history",
  scrollBehavior(to, from, savedPosition) {
      if (savedPosition) {
          return savedPosition
      } else {
          return {
              x: 0,
              y: 0
          }
      }
  }

疑问点:

在此次demo练习中,打印了一下钩子函数的执行顺序,发现一个疑问点(我对钩子函数理解也很浅显):
从page1进入page2时,先执行了page2的beforeRouteEnter和created方法,然后才执行page1的deactivated方法。
所以我把这两个初始化设置,放在了activated里面,而没有放在deactivated中

    this.$route.meta.isBack=false;
    this.isFirstEnter=false;

结束语:

为了解决这个前进刷新后退不刷新问题,让我整整苦恼了一周时间,想了很多方法,也没能解决。最后综合各个大佬经验,试验了很多次,才归结出这个比较‘low’的方法。
目前,我也是vue小白,也在探索着前进,如果这个方法能解决你遇到的难题,我很高兴。如果你认为的确很low,求轻喷。
demo在下方的GitHub中,欢迎star。
也欢迎大家提供意见和建议,谢谢大家

GitHub

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89913.html

相关文章

  • 架构师之路

    摘要:因为用户不用在第一次进入应用时下载所有代码,用户能更快的看到页面并与之交互。译高阶函数利用和来编写更易维护的代码高阶函数可以帮助你增强你的,让你的代码更具有声明性。知道什么时候和怎样使用高阶函数是至关重要的。 Vue 折腾记 - (10) 给axios做个挺靠谱的封装(报错,鉴权,跳转,拦截,提示) 稍微改改都能直接拿来用~~~哟吼吼,哟吼吼..... 如何无痛降低 if else 面...

    NikoManiac 评论0 收藏0
  • 再谈vue前进刷新后退刷新,include实现方法。

    摘要:并设置后退页面数组。跳转到或页面,返回后回到缓存页面,不刷新。由其他页面进入则刷新。反正目前可以实现想要的效果。 关于填坑vue的前进刷新与后退不刷新,网上有很多方法,基本都是利用 keep-alive,但是试了好多种方法都不尽人意,后来有人提示说基于keepalive include,试验了一下找到了些思路,暂时实验可行,分享下共同探讨学习,也许不是最佳解决方案,但确实有效。这里需要...

    layman 评论0 收藏0
  • 实现一个前端路由,如何实现浏览器的前进后退

    摘要:执行过程如下实现浏览器的前进后退第二个方法就是用两个栈实现浏览器的前进后退功能。我们使用两个栈,和,我们把首次浏览的页面依次压入栈,当点击后退按钮时,再依次从栈中出栈,并将出栈的数据依次放入栈。 showImg(https://segmentfault.com/img/bVbtK6U?w=1280&h=910); 如果要你实现一个前端路由,应该如何实现浏览器的前进与后退 ? 2. 问题...

    刘东 评论0 收藏0
  • 彻底理清前端页面应用(SPA)的实现原理 【精读源码】

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 随着React Vue前端框架的兴起,出现了Vue-router,react-router-dom等前端路由管理库,利用他们构建出来的单页面应用,也是越来越接近原生的体验,再也不是以前的点击标签跳转页面,刷新整个页面了,那么他们的原理是什么呢? 优质gitHub开源练手项目: ...

    xiaodao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<