最近用 Vue 做移動端頁面遇到一個問題,從列表頁進入詳情頁,再返回到列表頁,不管之前滾動到哪里,每次返回時都跳到列表最頂部。
這樣體驗肯定不好,期望的應(yīng)該是記住滾動條的位置,每次返回還是在原來的位置上,便于繼續(xù)瀏覽。
于是在網(wǎng)上搜解決方法,搜了一大圈看了 n 篇文章,都沒有說清楚。起碼我是沒有通過看一篇文章而完美解決,所以決定寫一篇詳細的親測可行的解決方案。
一共分三步:
- 給 router-view 添加 keep-alive
- 獲取并存儲當前 scrollTop
- 返回時取出并設(shè)置 scrollTop
100多位經(jīng)驗豐富的開發(fā)者參與,在 Github 上獲得了1000+個star的全棧全平臺開源項目想了解或參與嗎?
項目地址:https://github.com/cachecats/coderiver
一、給 router-view 添加 keep-alive
先貼出 keep-alive 官方文檔,不熟悉的可以先看看文檔。
<keep-alive> 包裹動態(tài)組件時,會緩存不活動的組件實例,而不是銷毀它們。所以在由詳情頁返回列表頁時,不讓列表頁刷新。
當組件在 <keep-alive> 內(nèi)被切換,它的 activated 和 deactivated 這兩個生命周期鉤子函數(shù)將會被對應(yīng)執(zhí)行。
設(shè)置 scrollTop 時是在 activated 方法里,有些文章說獲取 scrollTop 在 deactivated 方法里,但經(jīng)過測試,在 deactivated 方法里并不能準確的獲取 scrollTop 值,每次都是 0。具體原因暫時不深究,先解決問題。所以把獲取 scrollTop 值放在 item 的點擊事件函數(shù)里執(zhí)行。
二、獲取并存儲當前 scrollTop
頁面布局如下:
整個頁面是一個 <rounter-view> ,下面又分了兩個 tab,我們列表頁是一個組件,位于 title 和 導(dǎo)航欄之間的區(qū)域。
布局代碼:
<div class="wrapper" ref="wrapper">
<div class="title">我是標題</div>
<van-pull-refresh v-model="isRefresh" @refresh="onRefresh" ref="pullRefresh">
<van-list
ref="list"
class="list"
v-model="loadingMore"
:finished="finished"
finished-text="沒有更多了"
@load="onLoadMore"
>
<div class="item-wrapper" v-for="item in list" :key="item.id" @click="clickItem(item)" ref="item">
<div class="item">{{item}}</div>
</div>
</van-list>
</van-pull-refresh>
</div>
用到了 Vant-ui 的下拉刷新和上拉加載更多組件。
可以看到我一共給了四個 ref ,分別是最外層的 ref="list" ,下拉刷新組件 van-pull-refresh 的 ref="pullRefresh",列表組件 van-list 的 ref="list",和每個 item 的 ref="item"。
為什么給出這么多呢?因為這里有個大坑,也是我一直卡住的地方。
我們知道獲取滾動位置是用 scrollTop 這個屬性,下面我們就依次打印出這幾個元素的 scrollTop 。
clickItem(item) {
let wrapperScrollTop = this.$refs.wrapper.scrollTop;
let pullRefreshScrollTop = this.$refs.pullRefresh.scrollTop;
let listScrollTop = this.$refs.list.scrollTop;
let itemScrollTop = this.$refs.item.scrollTop;
console.log('wrapperScrollTop', wrapperScrollTop);
console.log('pullRefreshScrollTop', pullRefreshScrollTop);
console.log('listScrollTop', listScrollTop);
console.log('itemScrollTop', itemScrollTop);
this.$router.push({name: "detail", params: {data: item}})
},
放到 item 的點擊事件里觸發(fā)。得到的日志如下:
WTF?只有第一個 wrapperScrollTop 有值,其他的都 undefined !
我也不知道為啥,之前一直是獲取后三者的 scrollTop ,一直獲取不到,糾結(jié)了好久。為什么其他三個獲取不到我現(xiàn)在還沒整明白,知道原因的大佬可以指點一下。
知道了該獲取哪一個元素的 scrollTop 就簡單了,得到值只需存儲起來即可。
因為使用了 keep-alive,頁面被緩存起來了,所以 data 里的數(shù)據(jù)不會丟失,可以在 data 中聲明一個變量 scroll 存儲 scrollTop 的值。也可以使用 Vuex。
修改下 clickItem(item) 的代碼,將 scrollTop 的值存儲起來。
clickItem(item) {
let wrapperScrollTop = this.$refs.wrapper.scrollTop;
let pullRefreshScrollTop = this.$refs.pullRefresh.scrollTop;
let listScrollTop = this.$refs.list.scrollTop;
let itemScrollTop = this.$refs.item.scrollTop;
console.log('wrapperScrollTop', wrapperScrollTop);
console.log('pullRefreshScrollTop', pullRefreshScrollTop);
console.log('listScrollTop', listScrollTop);
console.log('itemScrollTop', itemScrollTop);
//存儲 scrollTop 的值
this.scroll = wrapperScrollTop;
this.$router.push({name: "detail", params: {data: item}})
},
三、返回時取出并設(shè)置 scrollTop
上面已經(jīng)介紹過了,使用 keep-alive 之后,每次返回頁面會調(diào)用 activated 生命周期方法,所以在這個方法里設(shè)置之前記住的 scrollTop,達到記住滾動位置的效果。
代碼很簡單,只有一句話:
activated() {
this.$refs.wrapper.scrollTop = this.scroll
}
完整的代碼如下:
<template>
<div class="wrapper" ref="wrapper">
<div class="title">我是標題</div>
<van-pull-refresh v-model="isRefresh" @refresh="onRefresh" ref="pullRefresh">
<van-list
ref="list"
class="list"
v-model="loadingMore"
:finished="finished"
finished-text="沒有更多了"
@load="onLoadMore"
>
<div class="item-wrapper" v-for="item in list" :key="item.id" @click="clickItem(item)" ref="item">
<div class="item">{{item}}</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</template>
<script>
export default {
components: {},
created() {
},
mounted() {
for (let i = 0; i < 15; i++) {
this.list.push(i)
}
},
data() {
return {
list: [], //列表數(shù)據(jù)
loadingMore: false, //加載更多是否顯示加載中
finished: false, //加載是否已經(jīng)沒有更多數(shù)據(jù)
isRefresh: false, //是否下拉刷新
scroll: 0,
}
},
activated() {
this.$refs.wrapper.scrollTop = this.scroll
},
deactivated() {
},
methods: {
clickItem(item) {
let wrapperScrollTop = this.$refs.wrapper.scrollTop;
let pullRefreshScrollTop = this.$refs.pullRefresh.scrollTop;
let listScrollTop = this.$refs.list.scrollTop;
let itemScrollTop = this.$refs.item.scrollTop;
console.log('wrapperScrollTop', wrapperScrollTop);
console.log('pullRefreshScrollTop', pullRefreshScrollTop);
console.log('listScrollTop', listScrollTop);
console.log('itemScrollTop', itemScrollTop);
this.scroll = wrapperScrollTop;
this.$router.push({name: "detail", params: {data: item}})
},
onRefresh() {
this.list = [];
this.finished = false;
setTimeout(() => {
for (let i = 0; i < 15; i++) {
this.list.push(i)
}
this.isRefresh = false
}, 2000)
},
//加載更多
onLoadMore() {
console.log('load more')
let newList = [];
for (let i = this.list.length; i < this.list.length + 15; i++) {
newList.push(i)
}
this.list = this.list.concat(newList)
this.loadingMore = false;
if (this.list.length > 50) {
this.finished = true
}
},
}
}
</script>
<style scoped lang="scss">
@import "../../public/css/index";
.wrapper {
width: 100%;
height: calc(100vh - 100px);
overflow-x: hidden;
box-sizing: border-box;
margin-bottom: px2rem(50);
.title{
font-size: px2rem(20);
padding: px2rem(10);
}
.list {
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
box-sizing: border-box;
.item-wrapper {
display: flex;
flex-direction: column;
font-size: px2rem(16);
margin: px2rem(8);
padding: px2rem(8);
background-color: white;
.item {
font-size: px2rem(16);
padding: px2rem(10);
}
}
}
}
</style>
好了,以上就是 Vue 返回記住滾動條位置的詳解。
全棧全平臺開源項目 CodeRiver
CodeRiver 是一個免費的項目協(xié)作平臺,愿景是打通 IT 產(chǎn)業(yè)上下游,無論你是產(chǎn)品經(jīng)理、設(shè)計師、程序員或是測試,還是其他行業(yè)人員,只要有好的創(chuàng)意、想法,都可以來 CodeRiver 免費發(fā)布項目,召集志同道合的隊友一起將夢想變?yōu)楝F(xiàn)實!
CodeRiver 本身還是一個大型開源項目,致力于打造全棧全平臺企業(yè)級精品開源項目。涵蓋了 React、Vue、Angular、小程序、ReactNative、Android、Flutter、Java、Node 等幾乎所有主流技術(shù)棧,主打代碼質(zhì)量。
目前已經(jīng)有近 100 名優(yōu)秀開發(fā)者參與,github 上的 star 數(shù)量將近 1000 個。每個技術(shù)棧都有多位經(jīng)驗豐富的大佬坐鎮(zhèn),更有兩位架構(gòu)師指導(dǎo)項目架構(gòu)。無論你想學什么語言處于什么技術(shù)水平,相信都能在這里學有所獲。
通過 高質(zhì)量源碼 + 博客 + 視頻,幫助每一位開發(fā)者快速成長。
項目地址:https://github.com/cachecats/coderiver
您的鼓勵是我們前行最大的動力,歡迎點贊,歡迎送小星星? ~