引入
在使用vue + vue-router開發SPA的時候,有沒有遇到過這樣的情況:當我們在列表頁和詳情頁之間切換的時候,如果列表頁不做快取,會導致每次從詳情頁返回時,列表頁都會重新載入。如下圖:
細心的朋友已經發現了,當從詳情頁返回列表頁的時候,列表頁過載了,這樣的體驗顯然不好,這時我們可以對列表頁進行快取處理。
keep-alive實現頁面快取
我們的專案不一定所有頁面都需要做快取處理,所以這裡介紹兩種按需快取的方法:
方法一:
首先在定義路由的時候配置 meta 欄位,自定義一個KeepAlive欄位作為該頁面是否快取的標記:
routes:[{
path: '/search',
name: 'search',
component: search,
meta: {
title: '搜尋列表頁',
keepAlive: true // 標記列表頁需要被快取
}
},
{
path: '/detail',
name: 'detail',
component: detail,
meta: {
title: '詳情頁',
// 詳情頁不需要做快取,所以不加keepAlive標記
}
}]
複製程式碼
由於<keep-alive>
元件不支援v-if指令,所以我們在App.vue中採用兩個<router-view>
的寫法,通過當前路由的keepAlive欄位來判斷是否對頁面進行快取:
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</div>
複製程式碼
方法二
使用<keep-alive>
提供的 exclude
或者 include
選項,此處我們使用 exclude
,在App.vue中:
<div id="app">
<keep-alive exclude="detail">
<router-view />
</keep-alive>
</div>
複製程式碼
需要注意的是,一定要給頁面元件加上相應的name,例如在detail.vue中:
<script>
export default {
name: 'detail', // 這個name要和keep-alive中的exclude選項值一致
...
}
</script>
複製程式碼
這麼寫就代表了在專案中除了name為detail的頁面元件外,其餘頁面都將進行快取。
優化:當使用者操作返回列表頁時,列表頁的滾動位置保持不變
只需要在 Router
例項中提供一個 scrollBehavior
方法:
export default new Router({
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
},
routes: [...],
})
複製程式碼
效果展示
效果看似很不錯,實現了列表頁的快取。但這其實並不靈活,比如返回首頁再進入這個搜尋頁的時候,由於沒做任何處理,所以這個搜尋頁它仍處於之前的狀態:然而,我希望凡是從首頁進入搜尋頁,頁面資料都需要重置回初始狀態,有沒有什麼方可以靈活控制頁面資料是否需要重置呢?這時我的腦海裡浮現了vue生態系統中的狀態管理庫vuex
。
藉助vuex使頁面更靈活
需求分析: 我們需要一個全域性的flag來控制每次進入快取頁時,資料是否需要重置,正好vuex
能做到。
vuex搞起來
安裝
npm install vuex --save
配置vuex
為了方便日後維護,可以建立一個store目錄專門存放vuex的模組程式碼,目錄結構參考下圖:
state.js:
const state = {
refreshSearch: true // 標記是否重新整理搜尋頁
}
export default state
複製程式碼
mutation.js
const matutaions = {
setRefreshSearch(state, flag) {
state.refreshSearch = flag
}
}
export default matutaions
複製程式碼
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations
})
複製程式碼
在入口檔案main.js中:
import store from './store' //這裡是指向store目錄中的index.js
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
複製程式碼
這樣,我們就相當於用vuex建立了一個用來判斷頁面是否需要重置的標記了。
為所欲為地重置你的快取頁中的資料
在哪裡重置
keep-alive
的元件有個特有的生命週期鉤子activated()
。activated()
會在keep-alive
的元件每次啟用時呼叫,而created()
只有建立的時候會被呼叫一次,再次啟用就不會被呼叫了。所以這裡我們需要在activated()
鉤子中重置我們的頁面資料。
在activated()中想重置就重置
這裡要藉助vuex
中的refreshSearch
標記來判斷是否需要重置
search.vue:(這個是需要快取的頁面)
<script>
import {mapState, mapMutations} from 'vuex' //vuex提供的對映函式,用來簡化程式碼的
export default {
activated() {
if (this.refreshSearch) {
// 若為true,則執行重置頁面等相關操作
this.fetchData();
} else {
this.reset(true);
}
},
methods:{
fetchData() {
// 獲取頁面資料
},
...mapMutations({
reset: 'setRefreshSearch' // 將 `this.reset()` 對映為 `this.$store.commit('setRefreshSearch')`
})
},
computed: {
...mapState([
'refreshSearch' // 對映 this.refreshSearch 為 this.$store.state.refreshSearch
]),
}
}
</script>
複製程式碼
當我們從搜尋頁去詳情頁時,希望搜尋頁快取,只需要把標記設為false:
methods: {
goDetail() {
this.reset(false) // 這樣返回搜尋頁的時候,搜尋頁就不會重置資料了
this.$router.push({path: '/detail'})
},
...mapMutations({
reset: 'setRefreshSearch'
})
}
複製程式碼
當我們從首頁去搜尋頁時,希望搜尋頁資料重置,只需把標記設為true:
methods: {
goSearch() {
this.reset(true) // 這樣去搜尋頁時資料就會被重置了
this.$router.push({path: '/search'})
},
...mapMutations({
reset: 'setRefreshSearch'
})
}
複製程式碼
效果預覽
總結
本文介紹了按需使用keep-alive
,以及藉助vuex
來控制keep-alive
的元件頁面的資料是否需要重置重新整理,希望對大家有幫助。
附送相關知識傳送門:
vue內建元件keep-alive