小編給大家分享一下怎么管理Vue中的緩存頁面,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
為企業(yè)提供網(wǎng)站設(shè)計制作、成都做網(wǎng)站、網(wǎng)站優(yōu)化、成都營銷網(wǎng)站建設(shè)、競價托管、品牌運營等營銷獲客服務(wù)。成都創(chuàng)新互聯(lián)擁有網(wǎng)絡(luò)營銷運營團隊,以豐富的互聯(lián)網(wǎng)營銷經(jīng)驗助力企業(yè)精準獲客,真正落地解決中小企業(yè)營銷獲客難題,做到“讓獲客更簡單”。自創(chuàng)立至今,成功用技術(shù)實力解決了企業(yè)“網(wǎng)站建設(shè)、網(wǎng)絡(luò)品牌塑造、網(wǎng)絡(luò)營銷”三大難題,同時降低了營銷成本,提高了有效客戶轉(zhuǎn)化率,獲得了眾多企業(yè)客戶的高度認可!<keep-alive> <router-view /> </keep-alive>
Vue中內(nèi)置的<keep-alive>組件可以幫助我們在開發(fā)SPA應(yīng)用時,通過把全部路由頁面進行緩存(當然也可以有針對性的緩存部分頁面),顯著提高頁面二次訪問速度,但是也給我們在某些場景帶來了困擾,其中包含兩個主要矛盾:
緩存頁面如何在合適的時機被銷毀 (keep-alive組件提供了三個參數(shù)來動態(tài)配置緩存狀態(tài),但是作用有限,后面分析)
同一個路徑如何緩存多個不同的頁面(同頁不同參),比如淘寶商品頁面繼續(xù)跳轉(zhuǎn)另一個商品頁面
本文主要圍繞這兩個問題探討,后文用問題一和問題二指代。
本文默認所有頁面都是keep-alive
當隨著業(yè)務(wù)邏輯變得復(fù)雜,路由棧也逐漸升高,理論上用戶可以無限的路由下去,不可避免的我們需要管理這些緩存在內(nèi)存中的頁面數(shù)據(jù),頁面數(shù)據(jù)包含兩部分,Vue實例和對應(yīng)的Vnode。查看 Vue 源碼中src/core/components/keep-alive.js關(guān)于緩存的定義
this.cache = Object.create(null) //用來緩存vnode cache[key] => Vnode this.keys = [] //用來記錄已緩存的vnode的key
緩存后并不會重用 Vnode,而是只用它上面掛載的 Vue 實例。
if (cache[key]) { vnode.componentInstance = cache[key].componentInstance //僅從緩存的vnode中獲取vue實例掛在到新的vnode上 // make current key freshest remove(keys, key) keys.push(key) }
為什么不用呢,因為有BUG,最早一版實現(xiàn)里確實是會直接使用緩存的 Vnode。
出自src/core/components/keep-alive.js init version
export default { created () { this.cache = Object.create(null) }, render () { const childNode = this.$slots.default[0] const cid = childNode.componentOptions.Ctor.cid if (this.cache[cid]) { const child = childNode.child = this.cache[cid].child //直接獲取緩存的vnode childNode.elm = this.$el = child.$el } else { this.cache[cid] = childNode } childNode.data.keepAlive = true return childNode }, beforeDestroy () { for (const key in this.cache) { this.cache[key].child.$destroy() } } }
我們需要管理的其實就是cache和keys,keep-alive提供了三個參數(shù)來動態(tài)管理緩存:
include - 只有名稱匹配的組件會被緩存。 exclude - 任何名稱匹配的組件都不會被緩存。 max - 最多可以緩存多少組件實例。
它們的作用非常簡單,源碼寫的也很簡單易讀:
所以當我們想要管理這些緩存時,簡單的方案就是操作這三個參數(shù),修改include和exclude來緩存或者清除某些緩存,但是需要注意的是它們匹配的是組件的name:
出自src/core/components/keep-alive.js
const name: ?string = getComponentName(componentOptions)
所以清除緩存是會無差別的把某個組件的所有實例全部清除,這顯然不滿足我們的需求。
max的邏輯則是超過較大值時清除棧底的緩存,
出自src/core/components/keep-alive.js:
if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) }
我們要解決問題一,官方提供給的API走不通,我們只能自己來了,我們需要的是解決兩個子問題:
什么時候銷毀
怎么銷毀
先看怎么銷毀,如果想銷毀一個實例很簡單,可以直接用 this.$destroy(), 這樣可以嗎,不行,這樣緩存cache和keys中依舊保留了原來的vnode和key,再次訪問時就會出現(xiàn)問題,vnode一直被留存,但是它身上的實例已經(jīng)被銷毀了,這時候在vue的update過程中就會再去創(chuàng)建一個vue實例,也就是說只要某個keep-alive的頁面調(diào)用過一次this.$destroy(),但是沒有清理緩存數(shù)組,這個頁面之后被重新渲染時就一定會重新創(chuàng)建一個實例,當然重新走全部的生命周期?,F(xiàn)象最終就是這個頁面就像是沒有被緩存一樣。
this.$destroy(); //不適合keep-alive組件
所以銷毀需要同時清理掉緩存cache和keys,下面定義了一個同時清除緩存的$keepAliveDestroy方法:
const dtmp = Vue.prototype.$destroy; const f = function() { if (this.$vnode && this.$vnode.data.keepAlive) { if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache) { if (this.$vnode.componentOptions) { var key = !isDef(this.$vnode.key) ? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '') : this.$vnode.key; var cache = this.$vnode.parent.componentInstance.cache; var keys = this.$vnode.parent.componentInstance.keys; if (cache[key]) { if (keys.length) { var index = keys.indexOf(key); if (index > -1) { keys.splice(index, 1); } } delete cache[key]; } } } } dtmp.apply(this, arguments); } Vue.prototype.$keepAliveDestroy = f;
那么什么時候銷毀呢,有兩個觸發(fā)時機:
replace時,頁面A --replace--> 頁面B (清除頁面A)
route back時 ,頁面A --push--> 頁面B --back--> 頁面A (清除頁面B)
replace 比較簡單,我們可以直接攔截router的replace方法,在該方法中清除掉當前頁面。(這里也有例外,比如切換Tab時,最后再說)
我們具體來看看route back這種情況,如果說我們的頁面上有一個返回鍵,那么在這里清除緩存是非常正確的時機,但是我們不能忽略瀏覽器自帶的返回鍵和安卓機上的物理返回鍵,這種情況考慮進來以后,僅使用返回鍵的方案就不能滿足了。
每次push或者replace是都增加query上一個參數(shù),來記錄當前深度
this.$router.push({ path:"/targer", query:{ stackLevel:Number(this.$route.query.stackLevel) + 1 } })
這個方案有明顯弊端,外部暴露一個參數(shù)是非常丑陋且危險的,用戶可以隨便修改,在進行網(wǎng)頁推廣時,業(yè)務(wù)去生產(chǎn)環(huán)境自己拷貝到的推廣鏈接也可能帶著一個奇怪的 https://xxx.com/foo?bar=123&stackLevel=13后綴。棄用
hack掉router的push和replace方法以后,每次跳轉(zhuǎn)的時候都可以給目標頁的vm掛載一個_stackLevel,這樣就解決了方案一的問題,不暴露給用戶,URL中不可見,也無法修改,但是我們不能忽視瀏覽器中另一個惡魔——刷新鍵,在刷新的時候URL不會變,但是vm實例就需要重新創(chuàng)建了,那么我們的棧深度標示也就丟失了。棄用
那么最終就是既可以對用戶不可見,又可以在刷新的時候得以保存。那就是history.state了,所以我們需要做的就是把stack深度保存到history.state中,它能夠完整的保存整個路由鏈條。
當我們獲取到目標頁面棧深度小于當前頁面時,我們就可以銷毀當前頁面了。
if(target.stack < current.stack){ current.$keepAliveDestroy(); }
可以在源碼中看到 src/core/components/keep-alive.js
const key: ?string = vnode.key == null // same constructor may get registered as different local components // so cid alone is not enough (#3269) ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key if (cache[key]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } else { cache[key] = vnode keys.push(key) // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } }
一個vnode如果沒有key才會使用組件名,所以默認緩存中的key是組件名,如果組件相同時,我們在每個頁面都有自己的key就可以解決這個問題了,如何實現(xiàn)每個頁面擁有自己的key呢。有兩個子問題:
如何做到
如何把key賦值給頁面的vnode
key = Date.now()
key = vm._stack + router.currentRoute.path 這個方案利用當前的棧高度+路徑名,為什么需要路徑名呢,因為replace的時候棧高度不變,只是路徑名變了。
目前有兩個方案給vue-router當前的Vnode的key來賦值:
這個方案實現(xiàn)比較簡單
//綁定key ... <router-view :key='$route.query.routerKey' /> ... //push時 this.$router.push({ path:"/foo", query:{ routerKey: Date.now() //隨機key } })
這種方式用起來非常簡單有效,但是缺點同樣也是會暴露一個奇怪的參數(shù)在URL中
在哪個階段給Vnode的key賦值呢,答案顯而易見,在keep-alive組件render函數(shù)進入前, src/core/components/keep-alive.js
... render () { const slot = this.$slots.default const vnode: VNode = getFirstComponentChild(slot) ...
我們可以hack掉keep-alive的render函數(shù),然后在這之前先把slot里的第一個子節(jié)點拿到以后,給它的key進行賦值,然后再調(diào)用 keep-alive的render:
const tmp = vm.$options.render //vm is keep-alive component instance vm.$options.render = function() { const slot = this.$slots.default; const vnode = getFirstComponentChild(slot) // vnode is a keep-alive-component-vnode if (historyShouldChange) { if (!isDef(vnode.key)) { if (isReplace) { vnode.key = genKey(router._stack) } else if (isPush()) { vnode.key = genKey(Number(router._stack) + 1) } else { vnode.key = genKey(Number(router._stack) - 1) } } } else { // when historyShouldChange is false should rerender only, should not create new vm ,use the same vnode.key issue#7 vnode.key = genKey(router._stack) } return tmp.apply(this, arguments) }
看完了這篇文章,相信你對“怎么管理Vue中的緩存頁面”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
網(wǎng)站標題:怎么管理Vue中的緩存頁面-創(chuàng)新互聯(lián)
文章網(wǎng)址:http://m.rwnh.cn/article2/djipic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、網(wǎng)站內(nèi)鏈、網(wǎng)站維護、網(wǎng)站建設(shè)、品牌網(wǎng)站設(shè)計、全網(wǎng)營銷推廣
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容