前言
在Vue構建的單頁面應用(SPA)中,路由模塊一般使用vue-router。vue-router不保存被切換組件的狀態,
它進行push或者replace時,舊組件會被銷毀,而新組件會被新建,走一遍完整的生命周期。
但有時候,我們有一些需求,比如跳轉到詳情頁面時,需要保持列表頁的滾動條的深度,等返回的時候依然在這個位置,這樣可以提高用戶體驗。
在Vue中,對於這種“頁面緩存”的需求,我們可以使用keep-alive組件來解決這個需求。
keep-alive
<keep-alive>
<router-view />
</keep-alive>
<keep-alive :include="['Home', 'User']"> <router-view /> </keep-alive>
vue實現前進刷新,后退不刷新
希望實現前進刷新、后退不刷新的效果。即加載過的界面能緩存起來(返回不用重新加載),關閉的界面能被銷毀掉(再進入時重新加載)。
例如對a->b->c 前進(b,c)刷新,c->b->a 后退(b,a)不刷新
知道路由是前進還是后退就好了,
這樣的話我就能在后退的時候讓from路由的keepAlive置為false ,
to路由的keepAlive置為ture,就能在再次前進時,重新加載之前這個keepAlive被置為false的路由了
但是這個需要集合鈎子函數來是實現
// App.vue
<div class="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
下面在router/index.js即我們的路由文件中,定義meta信息:
// list是我們的搜索結果頁面
{
path: '/list',
name: 'List',
component: resolve => require(['@/pages/list'], resolve),
meta: {
isUseCache: false, // 這個字段的意思稍后再說
keepAlive: true // 通過此字段判斷是否需要緩存當前組件
}
}
說這之前,先簡單說一下和緩存相關的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時是獲取數據的。
當我們從詳情頁返回時,我們把list頁面路由的isUseCache設置成true,這樣我們在返回list頁面時會使用緩存數據
// 詳情頁面的beforeRouteLeave鈎子函數
beforeRouteLeave (to, from, next) {
if (to.name == 'List') {
to.meta.isUseCache = true;
}
next();
}
我們這里是在即將離開detail頁面前判斷是否返回的列表頁。
如果是返回list頁面,則把list頁面路由的isUseCache字段設置成true。為什么這樣設置呢?
因為我們對list組件使用的keep-alive進行緩存組件,其默認就是使用緩存的。
而我們又在list組件的actived鈎子函數中進行了判斷:
只有在list頁面的isUseCache==false時才會清空原有數據並重新獲取數據。
所以此處設置isUseCache為true,此時返會list頁面是不會重新獲取數據的,而是使用的緩存數據。
detail返回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)
}
}
另外用Vuex來實現后退功能
然後在一篇博客中看到是用Vuex來寫的,所以這邊也自己demo了下:
就是下面的代碼了:
實現條件緩存:全局的include數組
只需要將B動態地從include數組中增加/刪除就行了
在Vuex中定義一個全局的緩存數組,待傳給include:
export default {
namespaced: true,
state: {
keepAliveComponents: [] // 緩存數組
},
mutations: {
keepAlive (state, component) {
// 注:防止重復添加(當然也可以使用Set)
!state.keepAliveComponents.includes(component) &&
state.keepAliveComponents.push(component)
},
noKeepAlive (state, component) {
const index = state.keepAliveComponents.indexOf(component)
index !== -1 &&
state.keepAliveComponents.splice(index, 1)
}
}
}
在父頁面中定義keep-alive,並傳入全局的緩存數組:
// App.vue
<div class="app">
<!--傳入include數組-->
<keep-alive :include="keepAliveComponents">
<router-view></router-view>
</keep-alive>
</div>
export default {
computed: {
...mapState({
keepAliveComponents: state => state.global.keepAliveComponents
})
}
}
緩存:在路由配置頁中,約定使用meta屬性keepAlive,值為true表示組件需要緩存。
在全局路由鈎子beforeEach中對該屬性進行處理,這樣一來,每次進入該組件,都進行緩存:
const router = new Router({
routes: [
{
path: '/A/B',
name: 'B',
component: B,
meta: {
title: 'B頁面',
keepAlive: true // 這里指定B組件的緩存性
}
}
]
})
router.beforeEach((to, from, next) => {
// 在路由全局鈎子beforeEach中,根據keepAlive屬性,統一設置頁面的緩存性
// 作用是每次進入該組件,就將它緩存
if (to.meta.keepAlive) {
store.commit('global/keepAlive', to.name)
}
})
取消緩存的時機:對緩存組件使用路由的組件層鈎子beforeRouteLeave。
因為B->A->B時不需要緩存B,所以可以認為:當B的下一個頁面不是C時取消B的緩存,那么下次進入B組件時B就是全新的:
export default {
name: 'B',
created () {
// ...設置滾動條在最頂部
},
beforeRouteLeave (to, from, next) {
// 如果下一個頁面不是詳情頁(C),則取消列表頁(B)的緩存
if (to.name !== 'C') {
this.$store.commit('global/noKeepAlive', from.name)
}
next()
}
}
因為B的條件緩存,是B自己的職責,所以最好把該業務邏輯寫在B的內部,而不是A中,這樣不至於讓組件之間的跳轉關系變得混亂。
一個需要注意的細節:因為keep-alive組件的include數組操作的對象是組件名、而不是路由名,
因此我們定義每一個組件時,都要顯式聲明name屬性,否則緩存不起作用。而且,一個顯式的name對Vue devtools有提示作用。

