keep-alive + vuex 讓快取的頁面靈活起來

HackerX發表於2019-04-17

引入

在使用vue + vue-router開發SPA的時候,有沒有遇到過這樣的情況:當我們在列表頁和詳情頁之間切換的時候,如果列表頁不做快取,會導致每次從詳情頁返回時,列表頁都會重新載入。如下圖:

keep-alive + vuex 讓快取的頁面靈活起來

細心的朋友已經發現了,當從詳情頁返回列表頁的時候,列表頁過載了,這樣的體驗顯然不好,這時我們可以對列表頁進行快取處理。

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: [...],
})
複製程式碼

效果展示

keep-alive + vuex 讓快取的頁面靈活起來
效果看似很不錯,實現了列表頁的快取。但這其實並不靈活,比如返回首頁再進入這個搜尋頁的時候,由於沒做任何處理,所以這個搜尋頁它仍處於之前的狀態:

keep-alive + vuex 讓快取的頁面靈活起來

然而,我希望凡是從首頁進入搜尋頁,頁面資料都需要重置回初始狀態,有沒有什麼方可以靈活控制頁面資料是否需要重置呢?這時我的腦海裡浮現了vue生態系統中的狀態管理庫vuex

藉助vuex使頁面更靈活

需求分析: 我們需要一個全域性的flag來控制每次進入快取頁時,資料是否需要重置,正好vuex能做到。

vuex搞起來

安裝

npm install vuex --save

配置vuex

為了方便日後維護,可以建立一個store目錄專門存放vuex的模組程式碼,目錄結構參考下圖:

keep-alive + 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,以及藉助vuex來控制keep-alive的元件頁面的資料是否需要重置重新整理,希望對大家有幫助。

附送相關知識傳送門:
vue內建元件keep-alive

vue-router滾動行為

生命週期鉤子activated

vuex狀態管理庫

相關文章