寫在開頭
終極解決方法柜去,直接看文章 解決循環(huán)跳轉(zhuǎn)頁面不刷新 部分
瀏覽器前進(jìn)刷新后退不刷新
這一部分用來祭奠我曾經(jīng)掉落在地的幾根頭發(fā)和逝去的時(shí)間灰嫉。
-
思路:
-
vue
頁面瀏覽記錄后退的能力是通過插件vue-router
實(shí)現(xiàn)的。而vue-rotuer
通過history.pushState
方法增加瀏覽記錄(或者history.replaceState
)诡蜓,然后通過監(jiān)聽popstate
事件來監(jiān)聽瀏覽器當(dāng)前會(huì)話頁面后退行為熬甫。 -
vue-rotuer
調(diào)用history.pushState
的時(shí)候會(huì)傳入一個(gè){key: _key /*唯一值*/}
對(duì)象,而捕獲popstate
事件時(shí)蔓罚,可以通過e.state.key
獲取這個(gè)唯一值 - 基于以上兩點(diǎn),我們可以通過收集 key 值來組成頁面之間的跳轉(zhuǎn)記錄瞻颂,然后通過
popstate
事件的key值來判斷頁面是前進(jìn)還是后退豺谈。頁面前進(jìn)時(shí),直接使用keep-alive
組件緩存當(dāng)前頁面贡这;如果頁面后退茬末,那么在回到上一個(gè)頁面之前從keep-alive
中清除當(dāng)前頁面緩存
-
-
實(shí)現(xiàn)代碼
<!-- 組件 keep-previous-alive 代碼 --> <template> <keep-alive> <slot /> </keep-alive> </template> <script> let routerHistory = []; function remove(arr, item) { if (arr.length) { const index = arr.indexOf(item); if (index > -1) { return arr.splice(index, 1); } } } // 清除 keep-alive 中的緩存 function pruneCacheEntry(cache, key, keys) { const cached = cache[key]; if (cached) { cached.componentInstance.$destroy(); } cache[key] = null; remove(keys, key); } function handleAfterEachRouter(to, from) { const historyState = window.history.state || {}; const historyStateKey = historyState.key; const index = routerHistory.indexOf(historyStateKey); if (index > -1) { // 回到舊頁面 routerHistory.splice(index + 1); } else { // 進(jìn)入新頁面 routerHistory.push(historyStateKey); } } function handleBeforeEachRouter(to, from, next) { // setTimeout 確保 handlePopstate 方法的調(diào)用是在進(jìn)入下個(gè)頁面之前 setTimeout(() => { next(); }, 0); } function setup(com) { const router = com.$router; if (router.beforeHooks.indexOf(handleBeforeEachRouter) <= -1) { router.beforeEach(handleBeforeEachRouter); } if (router.afterHooks.indexOf(handleAfterEachRouter) <= -1) { router.afterEach(handleAfterEachRouter); } } export default { name: 'keep-previous-alive', components: {}, props: {}, data() { return { keepAliveVnode: null, }; }, methods: { handlePopstate(e) { const state = e.state || {}; const stateKey = state.key; const vnode = this.$slots.default && this.$slots.default[0]; if (!vnode) return; if (stateKey === undefined) return; if (routerHistory.indexOf(stateKey) <= -1) return; // history back const keepAliveInstance = this.keepAliveVnode.componentInstance; const componentOptions = vnode.componentOptions; const key = vnode.key == null ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : "") : vnode.key; pruneCacheEntry(keepAliveInstance.cache, key, keepAliveInstance.keys); }, }, created() { window.addEventListener("popstate", this.handlePopstate); window.vm = this; }, mounted() { this.keepAliveVnode = this._vnode; setup(this); }, beforeDestroy() { window.removeEventListener("popstate", this.handlePopstate); }, }; </script> <style lang="scss" scoped> </style>
-
使用方法
<template> <div> <KeepPreviousAlive> <router-view v-if="$route.matched.some((r) => r.meta.keepAlive)"></router-view> </KeepPreviousAlive> <router-view v-if="!$route.matched.some((r) => r.meta.keepAlive)"></router-view> </div> </template>
-
優(yōu)點(diǎn)和缺陷
- 配置簡單:只要使用
KeepPreviousAlive
組件就可以,不用在緩存頁面的beforeRouteEnter
守衛(wèi)方法中判斷是否需要重新創(chuàng)建頁面盖矫。 - 缺點(diǎn):存在循環(huán)跳轉(zhuǎn)時(shí)丽惭,獲取到頁面數(shù)據(jù)都是緩存中的,不會(huì)重新刷新頁面辈双。例如:
- 用戶在 A 頁面责掏,調(diào)用
router.push('/b')
跳轉(zhuǎn)到 B 頁面 - 用戶已經(jīng)跳到 B 頁面,然后調(diào)用
router.push('/a')
跳轉(zhuǎn)到 A 頁面 - 這時(shí)候 A 頁面的數(shù)據(jù)還是從緩存中取到湃望,并不會(huì)進(jìn)入創(chuàng)建頁面的流程换衬。
- 用戶在 A 頁面责掏,調(diào)用
- 配置簡單:只要使用
<h3 id="3">解決循環(huán)跳轉(zhuǎn)頁面不刷新</h3>
在介紹解決方法之前,讓我們一起忘掉上一部分關(guān)于 KeepPreviousAlive
組件的實(shí)現(xiàn)证芭,來看下面一段代碼瞳浦。
// keep-alive 部分源碼
const key = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : "")
: vnode.key;
pruneCacheEntry(keepAliveInstance.cache, key, keepAliveInstance.keys);
從上面的代碼中可以知道 keep-alive
緩存的 key 值可以從 vnode.key
中獲取,而 vnode.key
是通過 v-bind:key="xxx"
傳入的废士,因此我們可以自己控制組件緩存的 key 值叫潦,那么當(dāng)我們需要讓某個(gè)已經(jīng)緩存的組件重新進(jìn)入創(chuàng)建頁面的時(shí)候,可以改變這個(gè)組件的 key 值官硝,讓它命中不了緩存矗蕊。
代碼示例:
<template>
<div>
<!-- 給緩存設(shè)置最大個(gè)數(shù)四敞,避免占用太多內(nèi)存 -->
<keep-alive :max="20">
<!-- 傳入緩存 key 值 -->
<router-view :key="$route.fullPath" v-if="$route.matched.some((r) => r.meta.keepAlive)" />
</keep-alive>
<router-view v-if="!$route.matched.some((r) => r.meta.keepAlive)" />
</div>
</template>
// 其他頁面
// 進(jìn)入a頁面時(shí),a頁面需要重新創(chuàng)建
this.$router.push({
path: '/a',
query: {
q: 'random querystring'
}
})