Vue中執(zhí)行“虛擬任務(wù)椦Τ埽”&&keep-alive組件實現(xiàn)頁面的緩存

???????在工作中营罢,我們經(jīng)常會碰到這樣的需求,從新聞列表頁面進入到新聞詳情頁饼齿,然后再從詳情頁返回列表頁時饲漾,需要記住上次列表頁面的滾動過的距離,并且再次進入列表頁面時缕溉,不會從新發(fā)起http請求考传。在vue中,要實現(xiàn)這個需求证鸥,相信你一定會想到keep-alive組件僚楞。

??????? 首先來解釋一下勤晚,什么是虛擬任務(wù)棧吧。我們的頁面中存在著一個記錄頁面間關(guān)系的“虛擬任務(wù)椚郑”运翼,當我們打開站點首頁的時候,獲取數(shù)據(jù)&渲染dom兴枯,同時會將當前渲染好的頁面push進這個棧中血淌,然后通過首頁,跳轉(zhuǎn)的到商品列表頁的時候财剖,執(zhí)行同樣的邏輯悠夯,執(zhí)行棧中又保存了列表頁的棧幀,并且將其置于棧頂部躺坟。如圖1所示沦补。當從列表頁面返回到首頁的時候,列表頁這個棧幀被彈出pop,恢復(fù)如圖2的效果咪橙。

圖1

圖2

???????keep-alive組件主要用于保留組件狀態(tài)或避免重新渲染夕膀。

一、頁面跳轉(zhuǎn)狀態(tài)保存原理

??????? 1美侦、所有的組件數(shù)據(jù)都會被保存下來产舞。
??????? 2、需要在組件中創(chuàng)建一個變量(會被keepAlive保存起來),通過這個變量來記錄當前頁面的滑動距離菠剩。
??????? 3易猫、當后退回該頁面的時候,使用這個變量來改變當前頁面的一個滑動距離具壮。
??????? ???? 1准颓、應(yīng)該在什么時候去改變當前頁面的滑動距離?
??????? ???? 2棺妓、可以在 組件的 activated (keep-aliave 組件被激活的時候被調(diào)用)方法中去執(zhí)行攘已。

二、實現(xiàn)頁面跳轉(zhuǎn)狀態(tài)保存

1怜跑、使用keep-alive保存頁面狀態(tài)

<transition :name="transitionName">
      <keep-alive :include="virtualTaskStack">
        <router-view />
      </keep-alive>
</transition>
data(){
  return {
    virtualTaskStack: {
      'Home'
    }
  }
}

??????? 這里的相關(guān)代碼如頁面之間的跳轉(zhuǎn)切換<transition>的實現(xiàn)邏輯已經(jīng)在另一篇文章《基于Vue實現(xiàn)頁面跳轉(zhuǎn)時的“滑動切換效果”》中講解過了样勃,你可以點擊連接查看。

2妆艘、借助“虛擬任務(wù)椡睿”實現(xiàn)虛擬任務(wù)入棧和出棧
??????? 我們可以按照這樣的邏輯,在頁面前進的過程中批旺,將其存放到虛擬棧中push幌陕,在后退的過程中,將當前頁面從棧中彈出pop汽煮。

watch: {
      // 監(jiān)聽路由對象搏熄,決定使用哪種過渡效果
      '$route'(to, from){
        // 獲取到攜帶的標記
        const routerType = to.params.routerType
        if(routerType === 'push'){
          // 跳轉(zhuǎn)頁面
          // 當進入新頁面的時候棚唆,保存新頁面的名稱到虛擬任務(wù)棧
          this.virtualTaskStack.push(to.name)
          this.transitionName = 'fold-left'
        } else{
          // 當執(zhí)行后退操作的時候,把最后一個頁面從任務(wù)棧中彈出
          this.virtualTaskStack.pop()
          // 后退頁面
          this.transitionName = 'fold-right'
        }
      }
}

??????? 請你重點關(guān)注下面兩行邏輯心例。

??????? 當進入新頁面的時候宵凌,保存新頁面的名稱到虛擬任務(wù)棧

 // 當進入新頁面的時候,保存新頁面的名稱到虛擬任務(wù)棧
this.virtualTaskStack.push(to.name)

???????當執(zhí)行后退操作的時候止后,把最后一個頁面從任務(wù)棧中彈出

 // 當執(zhí)行后退操作的時候瞎惫,把最后一個頁面從任務(wù)棧中彈出
 this.virtualTaskStack.pop()

3、應(yīng)用邏輯(Home組件)

???????構(gòu)建模版

<template>
  <div class="home"
    ref="home"
    @scroll="onScroll">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <button @click="toggle">toggle</button>
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
  </div>
</template>

???????設(shè)置狀態(tài)

data(){
       return {
         scrollTopValue: 0
       }
}

???????模版中存在一個滾動事件@scroll="onScroll"并設(shè)置了ref屬性值ref="home"译株。

???????監(jiān)聽滾動,獲取頁面滾動的高度

onScroll($event){
    this.scrollTopValue = $event.target.scrollTop
}

???????配置跳轉(zhuǎn)標記

toggle(){
   this.$router.push({
       name: 'About',
       params: {
           routerType: 'push'
       }
   })
}

???????調(diào)用activated鉤子函數(shù)瓜喇,設(shè)置當前頁面的滾動高度

/**
      * keepAlive 組件被激活時調(diào)用
      * 去為滑動模塊指定滑動距離
      * */
     activated: function () {
         this.$refs.home.scrollTop = this.scrollTopValue;
     }

???????整體的邏輯還是很清晰的,就是通過監(jiān)聽滾動事件歉糜,獲取頁面滾動的高度乘寒,在keep組件被激活的時候執(zhí)行activated鉤子函數(shù)中的邏輯,定位并保存頁面的的滾動距離匪补。當下次跳轉(zhuǎn)回來的時候可以記住上次頁面滾動過的位置伞辛。下面把相關(guān)的代碼貼出來:
App.vue

<template>
  <div id="app">
    <transition :name="transitionName">
      <keep-alive :include="virtualTaskStack">
        <router-view />
      </keep-alive>
    </transition>
  </div>
</template>

<script>
  export default{
    name: 'App',
    data(){
      return {
        transitionName: 'fold-left',
        virtualTaskStack: ['Home']
      }
    },
    watch: {
      // 監(jiān)聽路由對象,決定使用哪種過渡效果
      '$route'(to, from){
        // 獲取到攜帶的標記
        const routerType = to.params.routerType
        if(routerType === 'push'){
          // 跳轉(zhuǎn)頁面
          // 當進入新頁面的時候夯缺,保存新頁面的名稱到虛擬任務(wù)棧
          this.virtualTaskStack.push(to.name)
          this.transitionName = 'fold-left'
        } else{
          // 當執(zhí)行后退操作的時候蚤氏,把最后一個頁面從任務(wù)棧中彈出
          this.virtualTaskStack.pop()
          // 后退頁面
          this.transitionName = 'fold-right'
        }
      }
    }
  }
</script>
<style lang="scss">
  // push 頁面時:新頁面的進入動畫
  .fold-left-enter-active{
    animation-name: fold-left-in;
    animation-duration: .4s;
  }
  @keyframes fold-left-in{
    0% {
       transform: translate(100%, 0);
    }
    100% {
      transform: translate(0,0)
    }
  }

  // push 頁面時:原頁面的退出動畫
  .fold-left-leave-active{
    animation-name: fold-left-out;
    animation-duration: .4s;
  }
  @keyframes fold-left-out{
    0% {
       transform: translate(0, 0);
    }
    100% {
      transform: translate(-100%,0)
    }
  }

  // 后退頁面時: 即將展示頁面的動畫
  .fold-right-enter-active{
    animation-name: fold-right-in;
    animation-duration: .4s;
  }
  @keyframes fold-right-in{
    0% {
       transform: translate(-100%, 0);
    }
    100% {
      transform: translate(0,0)
    }
  }
  // 后退頁面時: 后退頁面的動畫
  .fold-right-leave-active{
    animation-name: fold-right-out;
    animation-duration: .4s;
  }
  @keyframes fold-right-out{
    0% {
       transform: translate(0, 0);
    }
    100% {
      transform: translate(100%,0)
    }
  }
</style>

Home.vue

<template>
  <div class="home"
    ref="home"
    @scroll="onScroll">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <button @click="toggle">toggle</button>
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
      <img src="http://t8.baidu.com/it/u=2247852322,986532796&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1592793437&t=3da58a8314290bf37b99253c355490fd" alt="">
  </div>
</template>


<script>
  export default {
     name: 'Home',
     data(){
       return {
         scrollTopValue: 0
       }
     },
     methods: {
       toggle(){
         this.$router.push({
           name: 'About',
           params: {
             routerType: 'push'
           }
         })
       },
       onScroll($event){
          this.scrollTopValue = $event.target.scrollTop
       }
     },
     /**
      * keepAlive 組件被激活時調(diào)用
      * 去為滑動模塊指定滑動距離
      * */
     activated: function () {
         this.$refs.home.scrollTop = this.scrollTopValue;
     }
  };
</script>

<style lang="scss">
  .home{
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
    overflow-y: auto;

  }
  img{
    width:100%;
  }
</style>

About.vue

<template>
  <div class="about">
    <button @click="toggle">toggle</button>
    <div> This page is About.vue</div>
  </div>
</template>
<script>
  export default{
    name: 'About',
    methods: {
      toggle(){
        this.$router.go(-1)
      }
    }
  }
</script>
<style lang="scss">
  .about{
    position: absolute;
  }
</style>

???????至此,我們分析就結(jié)束了喳逛,感謝您的閱讀瞧捌,祝你學(xué)習(xí)愉快!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末润文,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子殿怜,更是在濱河造成了極大的恐慌典蝌,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件头谜,死亡現(xiàn)場離奇詭異骏掀,居然都是意外死亡,警方通過查閱死者的電腦和手機柱告,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門截驮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人际度,你說我怎么就攤上這事葵袭。” “怎么了乖菱?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵坡锡,是天一觀的道長蓬网。 經(jīng)常有香客問我,道長鹉勒,這世上最難降的妖魔是什么帆锋? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮禽额,結(jié)果婚禮上锯厢,老公的妹妹穿的比我還像新娘。我一直安慰自己脯倒,他們只是感情好哲鸳,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盔憨,像睡著了一般徙菠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郁岩,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天婿奔,我揣著相機與錄音,去河邊找鬼问慎。 笑死萍摊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的如叼。 我是一名探鬼主播冰木,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笼恰!你這毒婦竟也來了踊沸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤社证,失蹤者是張志新(化名)和其女友劉穎逼龟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體追葡,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡腺律,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宜肉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匀钧。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谬返,靈堂內(nèi)的尸體忽然破棺而出之斯,到底是詐尸還是另有隱情,我是刑警寧澤朱浴,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布吊圾,位于F島的核電站达椰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏项乒。R本人自食惡果不足惜啰劲,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望檀何。 院中可真熱鬧蝇裤,春花似錦、人聲如沸频鉴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垛孔。三九已至藕甩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間周荐,已是汗流浹背狭莱。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留概作,地道東北人腋妙。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像讯榕,于是被迫代替她去往敵國和親骤素。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容