vue中,我們所要實現的一個場景就是:
1.搜尋頁面==>到搜尋結果頁時,搜尋結果頁面要重新獲取資料,
2.搜尋結果頁面==>點選進入詳情頁==>從詳情頁返回列表頁時,要儲存上次已經載入的資料和自動還原上次的瀏覽位置。
最近在專案中遇到這個問題,思考了幾套方案,總是不太完善。百度搜到的方案也基本都只能滿足一些很簡單的需求。對於複雜一些的情況,還是有些不完善的地方。以下是個人對於這種場景的一個摸索,也參考了百度。如有更好的方案,歡迎指出。
- 快取元件,vue2中提供了keep-alive。首先在我們的app.vue中定義keep-alive:
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"/>複製程式碼
這裡是根據路由中的meta源資訊中的keepAlive欄位來判斷當前路由元件是否需要快取。這裡的meta的keepAlive是我們自定義的,當然你也可以叫別的名字。
下面在router/index.js即我們的路由檔案中,定義meta資訊:
// list是我們的搜尋結果頁面
{
path: `/list`,
name: `List`,
component: resolve => require([`@/pages/list`], resolve),
meta: {
isUseCache: false, // 這個欄位的意思稍後再說
keepAlive: true // 通過此欄位判斷是否需要快取當前元件
}
},複製程式碼
上面的component: resolve => require([`@/pages/list`], resolve)這裡的元件引入方式可能和大家平時寫的有些不一樣,這裡是為了路由的懶載入用的,大家可以忽略。按照正常的import引入也可以,這個和本次的主題無關。如此一來,vue的路由會幫我們去快取list頁面。
- 重新整理資料or快取資料的實現:
說這之前,先簡單說一下和快取相關的vue鉤子函式。
設定了keepAlive快取的元件:
第一次進入:beforeRouterEnter ->created->…->activated->…->deactivated
後續進入時:beforeRouterEnter ->activated->deactivated
可以看出,只有第一次進入該元件時,才會走created鉤子,而需要快取的元件中activated是每次都會走的鉤子函式。所以,我們要在這個鉤子裡面去判斷,當前元件是需要使用快取的資料還是重新重新整理獲取資料。思路有了,下面我們來實現:
// list組價的activated鉤子
activated() {
// isUseCache為false時才重新重新整理獲取資料
// 因為對list使用keep-alive來快取元件,所以預設是會使用快取資料的
if(!this.$route.meta.isUseCache){
this.list = []; // 清空原有資料
this.onLoad(); // 這是我們獲取資料的函式
}
},複製程式碼
這裡的isUseCache 其實就是我們用來判斷是否需要使用快取資料的欄位,我們在list的路由的meta中已經預設設定為false,所以第一次進入list時是獲取資料的。
由於我們只要列表到詳情頁的時候快取列表頁的資料,所以我們要在列表頁的beforeRouteLeave的鉤子判斷,如果前往的是詳情頁面,則快取資料:
// 列表頁面的beforeRouteLeave鉤子函式
beforeRouteLeave (to, from, next) {
if (to.name == `Detail`) {
from.meta.isUseCache = true;
}
next();
},複製程式碼
現在,detail返回list頁面。list頁面已經被快取資料了,那麼現在如果前往search頁面後再前往list頁面時怎麼讓list頁面不使用快取資料而是獲取新資料呢?答案就在list頁面的activated鉤子中:
// list組價的activated鉤子
activated() {
// isUseCache為false時才重新重新整理獲取資料
// 因為對list使用keep-alive來快取元件,所以預設是會使用快取資料的
if(!this.$route.meta.isUseCache){
this.list = []; // 清空原有資料
this.onLoad(); // 這是我們獲取資料的函式
}
// 通過這個控制重新整理
this.$route.meta.isUseCache = false;
},複製程式碼
我們加了一行this.$route.meta.isUseCache=false;也就是從detail返回list後,將list的isUseCache欄位為false,而從detail返回list前,我們設定了list的isUseCache為true。所以,只有從detail返回list才使用快取資料,而其他頁面進入list是重新重新整理資料的。
至此,一個前進重新整理、後退返回的功能基本完成了。
如果場景再複雜一丟丟,比如,如果這個詳情頁是個訂單詳情,那麼在訂單詳情頁可能會有刪除訂單的操作。那麼刪除訂單操作後會返回訂單列表頁,是需要列表頁重新重新整理的。那麼我們需要此時在訂單詳情頁進行是否要重新整理的判斷。簡單改造一下詳情頁:
data () {
return {
isDel: false // 是否進行了刪除訂單的操作
}
},
beforeRouteLeave (to, from, next) {
if (to.name == `List`) {
// 根據是否刪除了訂單的狀態,進行判斷list是否需要使用快取資料
to.meta.isUseCache = !this.isDel;
}
next();
},
methods: {
deleteOrder () {
// 這裡是一些刪除訂單的操作
// 將狀態變為已刪除訂單
// 所以beforeRouteLeave鉤子中就會將list元件路由的isUseCache設定為false
// 所以此時再返回list時,list是會重新重新整理資料的
this.isDel = true;
this.$router.go(-1)
}
}複製程式碼
至此,算是解決了我的vue專案中的這個前進重新整理、後退快取資料和瀏覽位置的問題。
最後再提一下,頁面滾動位置的問題。
問題1:我們知道,在vue這種單頁應用中,如果你在a頁面滾動了一段距離後,此時前往b頁面後,b頁面也會停留在a頁面的滾動位置。這個問題的解決,我們可以利用router本身提供的功能來解決:
routes: [
{
path: `/detail`,
name: `Detail`,
component: resolve => require([`@/pages/detail`], resolve)
}
],
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = document.body.scrollTop;
}
return { x: 0, y: to.meta.savedPosition || 0 }
}
}複製程式碼
scrollBehavior是路由提供的基礎功能,這段函式寫的是:
1.如果通過瀏覽器自帶的前進後退按鈕切換的路由,那麼會自動使用瀏覽預設的回滾上次頁面的瀏覽位置。
2.如果是通過vue路由進行的頁面切換。例如a前往b,首先判斷a是不是通過keep-alive快取的元件,如果是,則在a路由的meta中新增一個savedPosition欄位,並且值為a的滾動位置。最後return的是頁面需要回滾的位置。如此一來,如果開啟一個頁面,該頁面的元件路由中meta.savedPosition為undefined的話,則頁面滾動到(0,0)的位置,這樣解決了問題1。那麼如果開啟一個頁面,它的路由的meta.savedPosition有值的話,則滾動到上次瀏覽的位置,因為meta.savedPosition儲存的就是上次瀏覽的位置。
如果覺得有幫助的話,就收藏一下吧!