最近用 Vue 做移動端頁面遇到一個問題,從列表頁進入詳情頁,再返回到列表頁,不管之前滾動到哪裡,每次返回時都跳到列表最頂部。
這樣體驗肯定不好,期望的應該是記住滾動條的位置,每次返回還是在原來的位置上,便於繼續瀏覽。
於是在網上搜解決方法,搜了一大圈看了 n 篇文章,都沒有說清楚。起碼我是沒有通過看一篇文章而完美解決,所以決定寫一篇詳細的親測可行的解決方案。
一共分三步:
- 給 router-view 新增 keep-alive
- 獲取並儲存當前 scrollTop
- 返回時取出並設定 scrollTop
100
多位經驗豐富的開發者參與,在 Github 上獲得了1000+
個star
的全棧全平臺開源專案想了解或參與嗎?
專案地址:https://github.com/cachecats/coderiver
一、給 router-view 新增 keep-alive
先貼出 keep-alive 官方文件,不熟悉的可以先看看文件。
<keep-alive>
包裹動態元件時,會快取不活動的元件例項,而不是銷燬它們。所以在由詳情頁返回列表頁時,不讓列表頁重新整理。
當元件在 <keep-alive>
內被切換,它的 activated
和 deactivated
這兩個生命週期鉤子函式將會被對應執行。
設定 scrollTop
時是在 activated
方法裡,有些文章說獲取 scrollTop
在 deactivated
方法裡,但經過測試,在 deactivated
方法裡並不能準確的獲取 scrollTop
值,每次都是 0。具體原因暫時不深究,先解決問題。所以把獲取 scrollTop
值放在 item 的點選事件函式裡執行。
二、獲取並儲存當前 scrollTop
頁面佈局如下:
整個頁面是一個 <rounter-view>
,下面又分了兩個 tab,我們列表頁是一個元件,位於 title 和 導航欄之間的區域。
佈局程式碼:
<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 的點選事件裡觸發。得到的日誌如下:
WTF?只有第一個 wrapperScrollTop
有值,其他的都 undefined
!
我也不知道為啥,之前一直是獲取後三者的 scrollTop
,一直獲取不到,糾結了好久。為什麼其他三個獲取不到我現在還沒整明白,知道原因的大佬可以指點一下。
知道了該獲取哪一個元素的 scrollTop
就簡單了,得到值只需儲存起來即可。
因為使用了 keep-alive
,頁面被快取起來了,所以 data
裡的資料不會丟失,可以在 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}})
},
複製程式碼
三、返回時取出並設定 scrollTop
上面已經介紹過了,使用 keep-alive
之後,每次返回頁面會呼叫 activated
生命週期方法,所以在這個方法裡設定之前記住的 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: [], //列表資料
loadingMore: false, //載入更多是否顯示載入中
finished: false, //載入是否已經沒有更多資料
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 是一個免費的專案協作平臺,願景是打通 IT 產業上下游,無論你是產品經理、設計師、程式設計師或是測試,還是其他行業人員,只要有好的創意、想法,都可以來 CodeRiver 免費釋出專案,召集志同道合的隊友一起將夢想變為現實!
CodeRiver 本身還是一個大型開源專案,致力於打造全棧全平臺企業級精品開源專案。涵蓋了 React、Vue、Angular、小程式、ReactNative、Android、Flutter、Java、Node 等幾乎所有主流技術棧,主打程式碼質量。
目前已經有近 100
名優秀開發者參與,github 上的 star
數量將近 1000
個。每個技術棧都有多位經驗豐富的大佬坐鎮,更有兩位架構師指導專案架構。無論你想學什麼語言處於什麼技術水平,相信都能在這裡學有所獲。
通過 高質量原始碼 + 部落格 + 視訊
,幫助每一位開發者快速成長。
專案地址:https://github.com/cachecats/coderiver
您的鼓勵是我們前行最大的動力,歡迎點贊,歡迎送小星星✨ ~