电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

如何管理Vue中的緩存頁面

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

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

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

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

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

本文默認所有頁面都是keep-alive

問題一 銷毀

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

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,最早一版實現里確實是會直接使用緩存的 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提供了三個參數來動態管理緩存:

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

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

所以當我們想要管理這些緩存時,簡單的方案就是操作這三個參數,修改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走不通,我們只能自己來了,我們需要的是解決兩個子問題:

什么時候銷毀 怎么銷毀 1. 怎么銷毀

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

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;2. 什么時候銷毀

那么什么時候銷毀呢,有兩個觸發時機:

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

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

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

2.1 方案一 使用route.query 記錄當前頁面棧深度

每次push或者replace是都增加query上一個參數,來記錄當前深度

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

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

2.2 方案二 使用Vue實例自身記錄當前棧深度

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

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

那么最終就是既可以對用戶不可見,又可以在刷新的時候得以保存。那就是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就可以解決這個問題了,如何實現每個頁面擁有自己的key呢。有兩個子問題:

如何做到唯一 如何把key賦值給頁面的vnode 1. 如何做到唯一1.1 時間戳、超大隨機數

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

key = vm._stack + router.currentRoute.path 這個方案利用當前的棧高度+路徑名,為什么需要路徑名呢,因為replace的時候棧高度不變,只是路徑名變了。

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

目前有兩個方案給vue-router當前的Vnode的key來賦值:

2.1 通過route.query動態綁定Key

這個方案實現比較簡單

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

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

2.2 通過獲取到Vnode直接賦值

在哪個階段給Vnode的key賦值呢,答案顯而易見,在keep-alive組件render函數進入前, src/core/components/keep-alive.js

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

我們可以hack掉keep-alive的render函數,然后在這之前先把slot里的第一個子節點拿到以后,給它的key進行賦值,然后再調用 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)}總結

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

演示Demo Sample Code

Bilibili演示視頻 感謝三連。

以上就是如何管理Vue中的緩存頁面的詳細內容,更多關于vue 緩存頁面的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
主站蜘蛛池模板: IHDW_TOSOKU_NEMICON_EHDW系列电子手轮,HC1系列电子手轮-上海莆林电子设备有限公司 | 双段式高压鼓风机-雕刻机用真空泵-绍兴天晨机械有限公司 | 股指期货-期货开户-交易手续费佣金加1分-保证金低-期货公司排名靠前-万利信息开户 | 广东恩亿梯电源有限公司【官网】_UPS不间断电源|EPS应急电源|模块化机房|电动汽车充电桩_UPS电源厂家(恩亿梯UPS电源,UPS不间断电源,不间断电源UPS) | 高压直流电源_特种变压器_变压器铁芯-希恩变压器定制厂家 | 空调风机,低噪声离心式通风机,不锈钢防爆风机,前倾皮带传动风机,后倾空调风机-山东捷风风机有限公司 | 海鲜池-专注海鲜鱼缸、移动海鲜缸、饭店鱼缸设计定做-日晟水族厂家 | 铝箔-铝板-花纹铝板-铝型材-铝棒管-上海百亚金属材料有限公司 | 中国产业发展研究网 - 提供行业研究报告 可行性研究报告 投资咨询 市场调研服务 | 山西3A认证|太原AAA信用认证|投标AAA信用证书-山西AAA企业信用评级网 | 空气能采暖,热泵烘干机,空气源热水机组|设备|厂家,东莞高温热泵_正旭新能源 | 急救箱-应急箱-急救包厂家-北京红立方医疗设备有限公司 | 锂电叉车,电动叉车_厂家-山东博峻智能科技有限公司 | 精雕机-火花机-精雕机 cnc-高速精雕机-电火花机-广东鼎拓机械科技有限公司 | 多功能三相相位伏安表-变压器短路阻抗测试仪-上海妙定电气 | 爱佩恒温恒湿测试箱|高低温实验箱|高低温冲击试验箱|冷热冲击试验箱-您身边的模拟环境试验设备技术专家-合作热线:400-6727-800-广东爱佩试验设备有限公司 | 活性炭-果壳木质煤质柱状粉状蜂窝活性炭厂家价格多少钱 | 铝箔袋,铝箔袋厂家,东莞铝箔袋,防静电铝箔袋,防静电屏蔽袋,防静电真空袋,真空袋-东莞铭晋让您的产品与众不同 | 2025福建平潭岛旅游攻略|蓝眼泪,景点,住宿攻略-趣平潭网 | 湖州织里童装_女童男童中大童装_款式多尺码全_织里儿童网【官网】-嘉兴嘉乐网络科技有限公司 | 球盟会·(中国)官方网站| 诸城网站建设-网络推广-网站优化-阿里巴巴托管-诸城恒泰互联 | 辐射仪|辐射检测仪|辐射巡测仪|个人剂量报警仪|表面污染检测仪|辐射报警仪|辐射防护网 | 存包柜厂家_电子存包柜_超市存包柜_超市电子存包柜_自动存包柜-洛阳中星 | 塑胶跑道_学校塑胶跑道_塑胶球场_运动场材料厂家_中国塑胶跑道十大生产厂家_混合型塑胶跑道_透气型塑胶跑道-广东绿晨体育设施有限公司 | 曙光腾达官网-天津脚手架租赁-木板架出租-移动门式脚手架租赁「免费搭设」 | PSI渗透压仪,TPS酸度计,美国CHAI PCR仪,渗透压仪厂家_价格,微生物快速检测仪-华泰和合(北京)商贸有限公司 | 东莞市超赞电子科技有限公司 全系列直插/贴片铝电解电容,电解电容,电容器 | 电机铸铝配件_汽车压铸铝合金件_发动机压铸件_青岛颖圣赫机械有限公司 | 不干胶标签-不干胶贴纸-不干胶标签定制-不干胶标签印刷厂-弗雷曼纸业(苏州)有限公司 | 小型气象站_车载气象站_便携气象站-山东风途物联网 | 液压油缸-液压缸厂家价格,液压站系统-山东国立液压制造有限公司 液压油缸生产厂家-山东液压站-济南捷兴液压机电设备有限公司 | 东莞螺杆空压机_永磁变频空压机_节能空压机_空压机工厂批发_深圳螺杆空压机_广州螺杆空压机_东莞空压机_空压机批发_东莞空压机工厂批发_东莞市文颖设备科技有限公司 | 合肥风管加工厂-安徽螺旋/不锈钢风管-通风管道加工厂家-安徽风之范 | 合肥触摸一体机_触摸查询机厂家_合肥拼接屏-安徽迅博智能科技 | 石家庄网站建设|石家庄网站制作|石家庄小程序开发|石家庄微信开发|网站建设公司|网站制作公司|微信小程序开发|手机APP开发|软件开发 | 广州展览设计公司_展台设计搭建_展位设计装修公司-众派展览装饰 广州展览制作工厂—[优简]直营展台制作工厂_展会搭建资质齐全 | 爱佩恒温恒湿测试箱|高低温实验箱|高低温冲击试验箱|冷热冲击试验箱-您身边的模拟环境试验设备技术专家-合作热线:400-6727-800-广东爱佩试验设备有限公司 | PCB厂|线路板厂|深圳线路板厂|软硬结合板厂|电路板生产厂家|线路板|深圳电路板厂家|铝基板厂家|深联电路-专业生产PCB研发制造 | 河南中整光饰机械有限公司-抛光机,去毛刺抛光机,精密镜面抛光机,全自动抛光机械设备 | 老城街小面官网_正宗重庆小面加盟技术培训_特色面馆加盟|牛肉拉面|招商加盟代理费用多少钱 |