最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何管理Vue中的緩存頁面

 更新時(shí)間:2021年02月06日 16:49:12   作者:OmniDebug  
這篇文章主要介紹了如何管理Vue中的緩存頁面,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
<keep-alive>
 <router-view />
</keep-alive>

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

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

本文主要圍繞這兩個問題探討,后文用問題一和問題二指代。

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

問題一 銷毀

當(dāng)隨著業(yè)務(wù)邏輯變得復(fù)雜,路由棧也逐漸升高,理論上用戶可以無限的路由下去,不可避免的我們需要管理這些緩存在內(nèi)存中的頁面數(shù)據(jù),頁面數(shù)據(jù)包含兩部分,Vue實(shí)例和對應(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 實(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í)是會直接使用緩存的 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提供了三個參數(shù)來動態(tài)管理緩存:

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

它們的作用非常簡單,源碼寫的也很簡單易讀:

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

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

const name: ?string = getComponentName(componentOptions)

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

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

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

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

我們要解決問題一,官方提供給的API走不通,我們只能自己來了,我們需要的是解決兩個子問題:

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

1. 怎么銷毀

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

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

所以銷毀需要同時(shí)清理掉緩存cache和keys,下面定義了一個同時(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í)候銷毀呢,有兩個觸發(fā)時(shí)機(jī):

  1. replace時(shí),頁面A --replace--> 頁面B (清除頁面A)
  2. route back時(shí) ,頁面A --push--> 頁面B --back--> 頁面A (清除頁面B)

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

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

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

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

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

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

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

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

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

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

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

if(target.stack < current.stack){
 current.$keepAliveDestroy();
}

問題二 同頁不同參緩存多個實(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)
 }
 }

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

  1. 如何做到唯一
  2. 如何把key賦值給頁面的vnode

1. 如何做到唯一

1.1 時(shí)間戳、超大隨機(jī)數(shù)

key = Date.now()

1.2 路由棧高度+路徑名

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

2. 如何把key賦值給頁面的vnode

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

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

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

//綁定key
...
<router-view :key='$route.query.routerKey' />
...


//push時(shí)
this.$router.push({
 path:"/foo",
 query:{
 	routerKey: Date.now() //隨機(jī)key
 }
})

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

2.2 通過獲取到Vnode直接賦值

在哪個階段給Vnode的key賦值呢,答案顯而易見,在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里的第一個子節(jié)點(diǎn)拿到以后,給它的key進(jìn)行賦值,然后再調(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)
}

總結(jié)

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

演示Demo Sample Code

Bilibili演示視頻 感謝三連。

以上就是如何管理Vue中的緩存頁面的詳細(xì)內(nèi)容,更多關(guān)于vue 緩存頁面的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Vue2?watch監(jiān)聽props的值

    詳解Vue2?watch監(jiān)聽props的值

    再次遇到監(jiān)聽子組件收到父組件傳過來的值,如果這個值變化,頁面中的值發(fā)現(xiàn)是不會跟著同步變化的,本文給大家介紹Vue2?watch監(jiān)聽props的值,感興趣的朋友一起看看吧
    2023-12-12
  • 在vue中import()語法不能傳入變量的問題及解決

    在vue中import()語法不能傳入變量的問題及解決

    這篇文章主要介紹了在vue中import()語法不能傳入變量的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue如何使用cdn加載資源加快打包速度

    Vue如何使用cdn加載資源加快打包速度

    外部的庫文件,可以使用CDN資源,或者別的服務(wù)器資源等,下面這篇文章主要給大家介紹了關(guān)于Vue如何使用cdn加載資源加快打包速度的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • vue父組件值變化但子組件不刷新的三種解決方案

    vue父組件值變化但子組件不刷新的三種解決方案

    父組件傳遞給子組件的數(shù)據(jù),如果是一個復(fù)雜對象(例如一個數(shù)組或?qū)ο螅?那么子組件只會監(jiān)聽對象的引用而不是對象的內(nèi)容,這意味著當(dāng)對象的內(nèi)容發(fā)生變化時(shí),子組件不會更新,本文給大家介紹了vue子組件不刷新的三種解決方案,需要的朋友可以參考下
    2024-03-03
  • vue項(xiàng)目使用高德地圖時(shí)報(bào)錯:AMap?is?not?defined解決辦法

    vue項(xiàng)目使用高德地圖時(shí)報(bào)錯:AMap?is?not?defined解決辦法

    這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目使用高德地圖時(shí)報(bào)錯:AMap?is?not?defined的解決辦法,"AMap is not defined"是一個錯誤提示,意思是在代碼中沒有找到定義的AMap,需要的朋友可以參考下
    2023-12-12
  • VUE中鼠標(biāo)滾輪使div左右滾動的方法詳解

    VUE中鼠標(biāo)滾輪使div左右滾動的方法詳解

    這篇文章主要給大家介紹了關(guān)于VUE中鼠標(biāo)滾輪使div左右滾動的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • vue實(shí)現(xiàn)商品列表的添加刪除實(shí)例講解

    vue實(shí)現(xiàn)商品列表的添加刪除實(shí)例講解

    在本篇內(nèi)容里小編給大家分享的是關(guān)于vue實(shí)現(xiàn)商品列表的添加刪除實(shí)例講解,有興趣的朋友們可以參考下。
    2020-05-05
  • Vue組件之間數(shù)據(jù)共享淺析

    Vue組件之間數(shù)據(jù)共享淺析

    本文章向大家介紹vue組件中的數(shù)據(jù)共享,主要包括vue組件中的數(shù)據(jù)共享使用實(shí)例、應(yīng)用技巧、基本知識點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-11-11
  • Vue3搭建組件庫開發(fā)環(huán)境的示例詳解

    Vue3搭建組件庫開發(fā)環(huán)境的示例詳解

    這篇文章給大家分享Vue3搭建組件庫開發(fā)環(huán)境,給大家講解依次搭建組件庫、example、文檔、cli,本文內(nèi)容是搭建組件庫的開發(fā)環(huán)境的過程,感興趣的朋友跟隨小編一起看看吧
    2022-11-11
  • Vue組件實(shí)現(xiàn)評論區(qū)功能

    Vue組件實(shí)現(xiàn)評論區(qū)功能

    這篇文章主要為大家詳細(xì)介紹了Vue組件實(shí)現(xiàn)評論區(qū)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評論

永清县| 连城县| 新津县| 湖口县| 宁陵县| 阿荣旗| 安平县| 涟水县| 尚志市| 牙克石市| 绥化市| 宁波市| 铜陵市| 罗定市| 改则县| 红桥区| 察哈| 卢湾区| 永修县| 洞头县| 若羌县| 静宁县| 冷水江市| 仪陇县| 松江区| 巴塘县| 长顺县| 二连浩特市| 龙游县| 白河县| 托克逊县| 汕头市| 文安县| 荆州市| 华池县| 壤塘县| 内乡县| 左贡县| 清远市| 屏南县| 丹棱县|