vue如何實現單頁緩存方案分析

實現全站的頁面緩存,前進刷新,返回走緩存,并且能記住上一頁的滾動位置,參考了很多技術實現,github上的導航組件實現的原理要么使用的keep-alive,要么參考了keep-alive的源碼,但是只用keep-alive沒法實現相同path,不同參數展示不同view,這就有點坑了,所以需要結合自己要實現的功能,適當改造keep-alive,為了實現每次前進都能刷新,返回走緩存還能自動定位的功能,文章陸續從以下幾個方面展開講:兩套技術方案可選,最后定的技術方案的原因,實現的功能和原理,踩過的坑

方案一:vue的keep-alive組件

?

具體使用如下:?

  <keep-alive max="10"><router-view/></keep-alive>

為什么這么使用?
如vue官網(https://cn.vuejs.org/v2/api/#...)介紹:

<keep-alive> 包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。和 <transition> 相似,<keep-alive> 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現在父組件鏈中。

當組件在 <keep-alive> 內被切換,它的 activated 和 deactivated 這兩個生命周期鉤子函數將會被對應執行。主要用于保留組件狀態或避免重新渲染。

因為緩存的需要通常出現在切換頁面時,所以就需要結合vue-router的router-view來實現

為什么keep-alive能實現緩存?
?


render () {const slot = this.$slots.defaultconst vnode: VNode = getFirstComponentChild(slot)const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check patternconst name: ?string = getComponentName(componentOptions)const { include, exclude } = thisif (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst 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.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode || (slot && slot[0])}

?

如上keep-alive源碼,其中render函數是這樣實現的,要渲染的試圖組件作為插槽內容被獲取到,當渲染到路徑匹配到的視圖組件時會根據vnode存儲的內容拿到對應的name,一次將這些組件實例放到變量cache中,這樣根據路由就可以找到緩存的vnode,返回給createComponent方法去執行initComponent,vue組件渲染這塊的代碼如下

function initComponent (vnode, insertedVnodeQueue) {if (isDef(vnode.data.pendingInsert)) {insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)vnode.data.pendingInsert = null}vnode.elm = vnode.componentInstance.$elif (isPatchable(vnode)) {invokeCreateHooks(vnode, insertedVnodeQueue)setScope(vnode)} else {// empty component root.// skip all element-related modules except for ref (#3455)registerRef(vnode)// make sure to invoke the insert hookinsertedVnodeQueue.push(vnode)}
}

?

這里會有?vnode.elm?緩存了?vnode?創建生成的 DOM 節點。所以對于首次渲染而言,除了在?<keep-alive>?中建立緩存,和普通組件渲染沒什么區別。從進入到返回的大致執行流程如下

前進-返回

能實現的功能
能夠把要緩存的組件渲染的vnode記到cache里邊,當返回的時候用緩存里邊的dom直接渲染,還有keep-alive組件提供的include?和?exclude屬性,可以有條件的緩存想緩存的組件,如果配置了?max?并且緩存的長度超過了這個max的值,還要從緩存中刪除第一個

存在的問題
存在的問題是存儲vnode節點的key是name,也就是定義路由時組件對應的name,這就會導致同樣的path,不同參數的時候打開的是從cache里邊拿到的vnode,會渲染出同樣的視圖出來,但是很多業務場景都是根據參數來顯示不同內容,而keep-alive底層并沒有對此做擴展,可以看下keep-alive源碼

 const key: ?string = vnode.key == null? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : ''): vnode.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}

?vnode.key就是路由里邊定義的name,所以要用這套方案來實現的根據不同參數展示不同視圖的功能就要對這里的key做改造,但是keep-alive是vue自帶的,沒法改底層,然后就誕生了我的第二套方案
?

方案二:navigation組件,scrollbehavior?

github上找到類似功能的組件vue-navigation,這個vue組件可以實現返回走緩存,底層原理跟keep-alive一樣,實際上是改寫了keep-alive組件,前進刷新時新增了一個參數VNK,這樣在路由發生變化的時候都會用給url帶一個參數,并且cache的key取值依賴這個參數,借鑒這個組件的思路,做了一個類似keep-alive的組件,其中key的值是getKey方法獲取的,改寫以后的render方法如下

 render () {var vnode = this.$slots.default ? this.$slots.default[0] : nullif (vnode) {vnode.key = vnode.key || (vnode.isComment ? 'comment' : vnode.tag)const { cache, keys } = thisvar key = getKey(this.$route, keyName)if (vnode.key.indexOf(key) === -1) {vnode.key = '__navigation-' + key + '-' + vnode.key}if (cache[key]) {if (vnode.key === cache[key].key) {vnode.componentInstance = cache[key].componentInstance} else {cache[key].componentInstance.$destroy()cache[key] = vnode}remove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode}

?getKey方法實現
//url上新增參數vnk的值

export function genKey() {// const t  = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'const t = 'xxxxxxxx'return t.replace(/[xy]/g, function (c) {const r = Math.random() * 16 | 0const v = c === 'x' ? r : (r & 0x3 | 0x8)return v.toString(16)})
}
//
export function getKey(route, keyName) {return `${route.name || route.path}?${route.query[keyName]}`
}

通過新寫一個install方法掛載這個導航組件到vue上就可以實現前進刷新,返回走緩存,并且可以配置最大緩存數,后續開源到github

最后剩下返回上一頁記住上一頁的位置,之所以沒有用開源的這個組件的記位置,是因為直接套用需要改整體布局,height:100%;樣式造成$(windows).scrollTop失效,整體考慮改造成本較大,還是使用了vue-router提供的scrollBehavior,在路由配置里引入

實現如下:

var scrollBehavior = async (to, from, savedPosition) => {if (savedPosition) {return savedPosition} else {return new Promise((resolve, reject) => {setTimeout(() => {resolve({ x: 0, y: to.meta.savedPosition || 0 })}, 300)})}
}
const router = new VueRouter({mode: 'history',scrollBehavior,routes: [{path: '',redirect: '/mobile/home.html',meta: {needMtaReport: true,parentsStyle: {height: '100%',minHeight: '100%'}}},{name: 'scienceCompetition',path: '/mobile/scienceCompetition.html',component: scienceCompetition}]
}

總結:

1.單頁緩存下js加載解析編譯執行的時間縮短了,返回的時候由于走緩存js腳本的占用時間完全可以忽略,從而整體上縮減了頁面的加載渲染時間

  2. 因為項目以前不是單頁,代碼里邊定義了很多全局變量或者全局事件綁定,改成單頁后全局變量的值依然存在,就會導致業務邏輯出現bug,所以使用單頁需要注意全局變量或是事件的謹慎使用,具體的踩坑記錄在https://www.cnblogs.com/after...

  3.通過push進入下一頁時,head里邊會累加前面頁面的靜態資源,訪問的頁面越多,最后的頁面掛的靜態的資源越多,返回的時候并不會減少已經加載的靜態資源,單頁緩存是典型的空間換時間的方案,內存的開銷比較大,能否對資源動態增減以及內存占用的優化一直在探索中,暫時沒有找到很好的解決方法。。。。。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450617.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450617.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450617.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言常用函數簡介

一、字符測試函數 isupper()測試字符是否為大寫英文字ispunct()測試字符是否為標點符號或特殊符號isspace()測試字符是否為空格字符isprint()測試字符是否為可打印字符islower()測試字符是否為小寫字母isgraphis()測試字符是否為可打印字符isdigit()測試字符是否為阿拉伯數字i…

thinkphp如何增加session的過期時間

原理&#xff1a;我們都知道session是建立在cookie的基礎上的&#xff0c;如果瀏覽器cookie清楚了&#xff0c;則tp就會重新建立一個session。 操作&#xff1a;直接增加瀏覽器的cookie的到期時間&#xff0c;就可以使tp的session增加。

需求心得

電路圖是人們為研究、工程規劃的需要。我們組項目需要設計實現一個矢量圖編輯器。在通過對變電站的電路圖進行矢量繪圖后&#xff0c;就可以通過矢量圖的縮放詳細信息。在分析需求后&#xff0c;寫下心得&#xff01; 分析需求主要有一下幾個步驟&#xff1a; 1. 獲取和引導需求…

IT部門不應該是一個后勤部門

管理上最大的問題在于不重視預算與核算的管理。從管理層到員工&#xff0c;很少有經營的念頭&#xff0c;只是一味地埋頭做事。西方企業總結了當今幾百年的經營理念&#xff0c;最終把企業一切活動的評價都歸結到唯一的、可度量的標準上&#xff1a;錢來度量。 by——華為 作為…

you need to resolve your current index first 解決辦法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 從一個分支A切換到另一個分支B后&#xff0c;對切換后的B分支進行pull操作&#xff0c;因為pull操作實際上包含了fetchmerge操作&#x…

C語言,一種如此美麗的語言

人們說足球是一種優美的體育運動&#xff0c;而當我們在綠茵場上看到羅納爾多那行云流水的帶球動作時&#xff0c;我們不能不承認這種說法。然而&#xff0c;對于我來說&#xff0c;這種運動之所以如此的賞心悅目&#xff0c;跟那些乖張的天才球星們關系并不是那么大&#xff0…

基于websocket的聊天實現邏輯(springboot)

websocket的知識點&#xff1a;當用戶建立socket連接請求之后&#xff0c;服務器會給客戶段建一個session&#xff08;非httpsession&#xff09;,這是是對客戶端的唯一識別碼&#xff0c;用于消息通信 第二上流程圖&#xff0c;流程圖解釋&#xff1a;用戶1要給用戶2發送消息…

Elasticsearch就這么簡單

Elasticsearch就這么簡單 Lucene就這么簡單轉載于:https://www.cnblogs.com/gaogaoyanjiu/p/9908520.html

大學生學編程系列」第五篇:自學編程需要多久才能找到工作?

很多編程初學者都會有這種疑問&#xff0c;自學學到什么程度或者學多久能夠找到工作&#xff0c;這種問題沒有統一答案&#xff0c;因為每個人的出發時候的基礎以及在學習過程中掌握的程度不盡相同&#xff0c;也會導致結果不一樣&#xff0c;只能說要看個人的造化了&#xff0…

chrome 谷歌瀏覽器怎么添加Axure擴展

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 工具/原料 谷歌瀏覽器Axure RP Extension for Chrome方法/步驟 百度搜索Axure RP&#xff0c;下載Axure RP&#xff0c;并進行安裝 安裝后…

配置nginx-rtmp流媒體服務器(寶塔面板配置教程)

參考文檔&#xff1a;https://www.kancloud.cn/jiangguowu/kfjsdkfjskd/1209896 1.在寶塔面板中安裝帶nginx的服務器 2.在寶塔面板中卸載nginx&#xff08;因為nginx-rtmp和nginx的配置不同&#xff0c;并且寶塔面板中不支持安裝nginx-rtmp&#xff09; 3.開始預下載nginx &a…

C語言的應用范圍和發展前途簡介

C一般用來底層開發&#xff0c;如操作系統&#xff0c;嵌入式開發&#xff0c;或者要求效率&#xff0c;高可移植性的地方。C對人要求很高&#xff0c;程序員要考慮的地方太多。他的特點就是每一個字節都可以精確控制&#xff0c;不象C&#xff0c;編譯器為你自動加的東西太多&…

css控制div等比高度

在移動端開發中&#xff0c;在banner輪播圖未加載出來之前&#xff0c;banner層是不占文檔流高度的&#xff0c;當從服務器獲取完banner數據&#xff0c;展示的時候&#xff0c;banner層因為有了內容 所以會撐開&#xff0c;導致banner層下面的內容也隨之移動&#xff0c;為解決…

2018杭州云棲大會,梁勝博士的演講PPT來啦!

2019獨角獸企業重金招聘Python工程師標準>>> 2018杭州云棲大會已經結束&#xff0c;Rancher作為阿里云的緊密合作伙伴&#xff0c;Rancher Labs聯合創始人兼CEO梁勝博士&#xff0c;在9月21日上午受邀出席大會并作題為**“如何能讓每個人都用Kubernetes和Service Me…

No Identifier specified for entity的解決辦法

見&#xff1a;http://blog.csdn.net/u011617875/article/details/18550305 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 No Identifier specified for entity的錯誤IdGeneratedVal…

利用jquery修改elment的自定義組件多選框el-select(修改多選框的顏色)

先上圖片 一實現邏輯 我們知道element顏色默認為灰色&#xff0c;首先便簽名稱是唯一的&#xff0c;我的實現邏輯是后端傳給前端 含有顏色&#xff0c;名稱的數組&#xff0c;然后vue記錄一個對象{名稱 > 顏色}&#xff0c;當用戶選擇標簽之后&#xff0c;觸發 點擊事件&…

怎樣在C語言程序中使用功能鍵和箭頭鍵?

在程序中使用功能鍵和箭頭鍵可以使程序更容易使用。箭頭鍵可用來移動光標&#xff0c;而功能鍵使用戶能做一些特殊的事情&#xff0c;還可用來替代一些經常要鍵入的字符序列。然而&#xff0c;與其它“特殊”功能一樣&#xff0c;C語言本身并沒有提供讀入功能鍵和箭頭鍵的標準方…

中文地址

2019獨角獸企業重金招聘Python工程師標準>>> 轉載于:https://my.oschina.net/u/2935389/blog/2209087

python+selenium十:selenium的二次封裝

pythonselenium十&#xff1a;基于原生selenium的二次封裝 from selenium import webdriverfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.select import Sele…

TDD開發模式實現代碼功能邏輯(自己總結,持續更新)

1.先寫測試 2.要使程序盡快的通過&#xff08;及早交付&#xff09; 3.優化程序結構&#xff0c;盡量使程序盡量快的運行 4.不要怕修改&#xff0c;單元測試會保證接口的正常運行 5.能通過測試后再去重構&#xff08;消除冗余&#xff0c;優化程序設計&#xff09; 6.用盡…