???????在工作中营罢,我們經(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的效果咪橙。
???????
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í)愉快!