国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

如何管理Vue中的緩存頁(yè)面

瀏覽:101日期:2022-10-06 10:53:23

<keep-alive> <router-view /></keep-alive>

Vue中內(nèi)置的<keep-alive>組件可以幫助我們?cè)陂_(kāi)發(fā)SPA應(yīng)用時(shí),通過(guò)把全部路由頁(yè)面進(jìn)行緩存(當(dāng)然也可以有針對(duì)性的緩存部分頁(yè)面),顯著提高頁(yè)面二次訪問(wèn)速度,但是也給我們?cè)谀承﹫?chǎng)景帶來(lái)了困擾,其中包含兩個(gè)主要矛盾:

緩存頁(yè)面如何在合適的時(shí)機(jī)被銷毀 (keep-alive組件提供了三個(gè)參數(shù)來(lái)動(dòng)態(tài)配置緩存狀態(tài),但是作用有限,后面分析) 同一個(gè)路徑如何緩存多個(gè)不同的頁(yè)面(同頁(yè)不同參),比如淘寶商品頁(yè)面繼續(xù)跳轉(zhuǎn)另一個(gè)商品頁(yè)面

本文主要圍繞這兩個(gè)問(wèn)題探討,后文用問(wèn)題一和問(wèn)題二指代。

本文默認(rèn)所有頁(yè)面都是keep-alive

問(wèn)題一 銷毀

當(dāng)隨著業(yè)務(wù)邏輯變得復(fù)雜,路由棧也逐漸升高,理論上用戶可以無(wú)限的路由下去,不可避免的我們需要管理這些緩存在內(nèi)存中的頁(yè)面數(shù)據(jù),頁(yè)面數(shù)據(jù)包含兩部分,Vue實(shí)例和對(duì)應(yīng)的Vnode。查看 Vue 源碼中src/core/components/keep-alive.js關(guān)于緩存的定義

this.cache = Object.create(null) //用來(lái)緩存vnode cache[key] => Vnode this.keys = [] //用來(lái)記錄已緩存的vnode的key

緩存后并不會(huì)重用 Vnode,而是只用它上面掛載的 Vue 實(shí)例。

if (cache[key]) { vnode.componentInstance = cache[key].componentInstance //僅從緩存的vnode中獲取vue實(shí)例掛在到新的vnode上 // make current key freshest remove(keys, key) keys.push(key)}

為什么不用呢,因?yàn)橛蠦UG,最早一版實(shí)現(xiàn)里確實(shí)是會(huì)直接使用緩存的 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() } }}

我們需要管理的其實(shí)就是cache和keys,keep-alive提供了三個(gè)參數(shù)來(lái)動(dòng)態(tài)管理緩存:

include - 只有名稱匹配的組件會(huì)被緩存。exclude - 任何名稱匹配的組件都不會(huì)被緩存。max - 最多可以緩存多少組件實(shí)例。

它們的作用非常簡(jiǎn)單,源碼寫(xiě)的也很簡(jiǎn)單易讀:

所以當(dāng)我們想要管理這些緩存時(shí),簡(jiǎn)單的方案就是操作這三個(gè)參數(shù),修改include和exclude來(lái)緩存或者清除某些緩存,但是需要注意的是它們匹配的是組件的name:

出自src/core/components/keep-alive.js

const name: ?string = getComponentName(componentOptions)

所以清除緩存是會(huì)無(wú)差別的把某個(gè)組件的所有實(shí)例全部清除,這顯然不滿足我們的需求。

max的邏輯則是超過(guò)最大值時(shí)清除棧底的緩存,

出自src/core/components/keep-alive.js:

if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode)}

我們要解決問(wèn)題一,官方提供給的API走不通,我們只能自己來(lái)了,我們需要的是解決兩個(gè)子問(wèn)題:

什么時(shí)候銷毀 怎么銷毀 1. 怎么銷毀

先看怎么銷毀,如果想銷毀一個(gè)實(shí)例很簡(jiǎn)單,可以直接用 this.$destroy(), 這樣可以嗎,不行,這樣緩存cache和keys中依舊保留了原來(lái)的vnode和key,再次訪問(wèn)時(shí)就會(huì)出現(xiàn)問(wèn)題,vnode一直被留存,但是它身上的實(shí)例已經(jīng)被銷毀了,這時(shí)候在vue的update過(guò)程中就會(huì)再去創(chuàng)建一個(gè)vue實(shí)例,也就是說(shuō)只要某個(gè)keep-alive的頁(yè)面調(diào)用過(guò)一次this.$destroy(),但是沒(méi)有清理緩存數(shù)組,這個(gè)頁(yè)面之后被重新渲染時(shí)就一定會(huì)重新創(chuàng)建一個(gè)實(shí)例,當(dāng)然重新走全部的生命周期。現(xiàn)象最終就是這個(gè)頁(yè)面就像是沒(méi)有被緩存一樣。

this.$destroy(); //不適合keep-alive組件

所以銷毀需要同時(shí)清理掉緩存cache和keys,下面定義了一個(gè)同時(shí)清除緩存的$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;2. 什么時(shí)候銷毀

那么什么時(shí)候銷毀呢,有兩個(gè)觸發(fā)時(shí)機(jī):

replace時(shí),頁(yè)面A --replace--> 頁(yè)面B (清除頁(yè)面A) route back時(shí) ,頁(yè)面A --push--> 頁(yè)面B --back--> 頁(yè)面A (清除頁(yè)面B)

replace 比較簡(jiǎn)單,我們可以直接攔截router的replace方法,在該方法中清除掉當(dāng)前頁(yè)面。(這里也有例外,比如切換Tab時(shí),最后再說(shuō))

我們具體來(lái)看看route back這種情況,如果說(shuō)我們的頁(yè)面上有一個(gè)返回鍵,那么在這里清除緩存是非常正確的時(shí)機(jī),但是我們不能忽略瀏覽器自帶的返回鍵和安卓機(jī)上的物理返回鍵,這種情況考慮進(jìn)來(lái)以后,僅使用返回鍵的方案就不能滿足了。

2.1 方案一 使用route.query 記錄當(dāng)前頁(yè)面棧深度

每次push或者replace是都增加query上一個(gè)參數(shù),來(lái)記錄當(dāng)前深度

this.$router.push({ path:'/targer', query:{ stackLevel:Number(this.$route.query.stackLevel) + 1 }})

這個(gè)方案有明顯弊端,外部暴露一個(gè)參數(shù)是非常丑陋且危險(xiǎn)的,用戶可以隨便修改,在進(jìn)行網(wǎng)頁(yè)推廣時(shí),業(yè)務(wù)去生產(chǎn)環(huán)境自己拷貝到的推廣鏈接也可能帶著一個(gè)奇怪的 https://xxx.com/foo?bar=123&stackLevel=13后綴。棄用

2.2 方案二 使用Vue實(shí)例自身記錄當(dāng)前棧深度

hack掉router的push和replace方法以后,每次跳轉(zhuǎn)的時(shí)候都可以給目標(biāo)頁(yè)的vm掛載一個(gè)_stackLevel,這樣就解決了方案一的問(wèn)題,不暴露給用戶,URL中不可見(jiàn),也無(wú)法修改,但是我們不能忽視瀏覽器中另一個(gè)惡魔——刷新鍵,在刷新的時(shí)候URL不會(huì)變,但是vm實(shí)例就需要重新創(chuàng)建了,那么我們的棧深度標(biāo)示也就丟失了。棄用

2.3 方案三 使用history.state記錄棧深度

那么最終就是既可以對(duì)用戶不可見(jiàn),又可以在刷新的時(shí)候得以保存。那就是history.state了,所以我們需要做的就是把stack深度保存到history.state中,它能夠完整的保存整個(gè)路由鏈條。

當(dāng)我們獲取到目標(biāo)頁(yè)面棧深度小于當(dāng)前頁(yè)面時(shí),我們就可以銷毀當(dāng)前頁(yè)面了。

if(target.stack < current.stack){ current.$keepAliveDestroy();}問(wèn)題二 同頁(yè)不同參緩存多個(gè)實(shí)例

可以在源碼中看到 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) } }

一個(gè)vnode如果沒(méi)有key才會(huì)使用組件名,所以默認(rèn)緩存中的key是組件名,如果組件相同時(shí),我們?cè)诿總€(gè)頁(yè)面都有自己的key就可以解決這個(gè)問(wèn)題了,如何實(shí)現(xiàn)每個(gè)頁(yè)面擁有自己的key呢。有兩個(gè)子問(wèn)題:

如何做到唯一 如何把key賦值給頁(yè)面的vnode 1. 如何做到唯一1.1 時(shí)間戳、超大隨機(jī)數(shù)

key = Date.now()1.2 路由棧高度+路徑名

key = vm._stack + router.currentRoute.path 這個(gè)方案利用當(dāng)前的棧高度+路徑名,為什么需要路徑名呢,因?yàn)閞eplace的時(shí)候棧高度不變,只是路徑名變了。

2. 如何把key賦值給頁(yè)面的vnode

目前有兩個(gè)方案給vue-router當(dāng)前的Vnode的key來(lái)賦值:

2.1 通過(guò)route.query動(dòng)態(tài)綁定Key

這個(gè)方案實(shí)現(xiàn)比較簡(jiǎn)單

//綁定key...<router-view :key=’$route.query.routerKey’ />...//push時(shí)this.$router.push({ path:'/foo', query:{ routerKey: Date.now() //隨機(jī)key }})

這種方式用起來(lái)非常簡(jiǎn)單有效,但是缺點(diǎn)同樣也是會(huì)暴露一個(gè)奇怪的參數(shù)在URL中

2.2 通過(guò)獲取到Vnode直接賦值

在哪個(gè)階段給Vnode的key賦值呢,答案顯而易見(jiàn),在keep-alive組件render函數(shù)進(jìn)入前, src/core/components/keep-alive.js

... render () { const slot = this.$slots.default const vnode: VNode = getFirstComponentChild(slot)...

我們可以hack掉keep-alive的render函數(shù),然后在這之前先把slot里的第一個(gè)子節(jié)點(diǎn)拿到以后,給它的key進(jìn)行賦值,然后再調(diào)用 keep-alive的render:

const tmp = vm.$options.render //vm is keep-alive component instancevm.$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)}總結(jié)

通過(guò)以上對(duì)于問(wèn)題的分析,我們就解決了自動(dòng)管理緩存的核心難題。本文是對(duì)開(kāi)源庫(kù) vue-router-keep-alive-helper 的一次總結(jié),此庫(kù)是款簡(jiǎn)單易用的keep-alive緩存自動(dòng)化管理工具,從此告別Vue緩存管理難題。如果對(duì)你有用,感謝慷慨Star。

演示Demo Sample Code

Bilibili演示視頻 感謝三連。

以上就是如何管理Vue中的緩存頁(yè)面的詳細(xì)內(nèi)容,更多關(guān)于vue 緩存頁(yè)面的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Vue
相關(guān)文章:
主站蜘蛛池模板: 黄色毛片视频在线观看 | 中文字幕一区中文亚洲 | 久久精品一品道久久精品9 久久精品一区 | 亚洲国产精品视频 | 久久久国产一区二区三区丝袜 | 国产成人精品在线观看 | 日本精品视频一视频高清 | 免费特黄一级欧美大片在线看 | 国产精品视频网址 | 怡红院最新免费全部视频 | 国产成人综合高清在线观看 | 国产成人精品永久免费视频 | 性色欧美xo影院 | 午夜伊人网 | 日韩精品一区二区三区视频 | 久久免费视屏 | 加勒比在线免费视频 | 亚洲精品亚洲一区二区 | 99精品视频在线观看免费播放 | 中文字幕一区二区三区久久网站 | 午夜影院啪啪 | 99国产在线 | 国产成人tv在线观看 | 欧美丝袜xxxxx在线播放 | 国产另类视频 | 天堂色视频| 国产免费一级高清淫曰本片 | 亚洲国产欧美一区二区欧美 | 美女一级片 | 精品色综合 | 成人午夜在线视频 | a级国产乱理伦片在线观看国 | 女人国产香蕉久久精品 | 毛片手机在线视频免费观看 | 国产成人麻豆tv在线观看 | 日韩免费一区二区三区 | 波多野结衣一区二区三区在线观看 | 国产精品亚洲精品日韩已方 | 欧美视频第一页 | 久久精品男人的天堂 | 日本一级毛片片在线播放 |