H5 頁面列表緩存方案

大家好,我是若川(點這里加我微信?ruochuan12,長期交流學習)。今天給大家介紹一下關于h5頁面的列表緩存方案。感謝屏幕前的你一直關注著我。

點擊下方卡片關注我、加個星標,或者查看源碼等系列文章。學習源碼整體架構系列、年度總結、JS基礎系列


前言

在 H5 日常開發中,會經常遇到列表點擊進入詳情頁面然后返回列表的情況,對于電商類平臺尤為常見,像我們平常用的淘寶、京東等電商平臺都是做了緩存,而且不只是列表,很多地方都用到了緩存。但剛才說的都是 App,在原生 App 中,頁面是一層層的 View,蓋在?LastPage?上,天然就能夠保存上一個頁面的狀態,而 H5 不同,從詳情返回到列表后,狀態會被清除掉,重新走一遍生命周期,會重新發起請求,會有新的狀態寫入,對于分頁接口,列表很長,當用戶翻了好幾頁后,點擊詳情看看商品詳情后再返回列表,此時頁面回到第一頁,這樣用戶體驗很差,如果在進入詳情的時候將列表數據緩存起來,返回列表的時候用緩存數據,而不是重新請求數據,停留在離開列表頁時的瀏覽位置;或者是能夠像 App 那樣,將頁面一層層堆疊在?LastPage?上,返回的時候展示對應的頁面,這樣用戶體驗會好很多,本文簡單介紹一下在自己在做列表緩存的時候考慮的幾點,后附簡單實現。

思考

狀態丟失的原因

通常在頁面開發中,我們是通過路由去管理不同的頁面,常用的路由庫也有很多,譬如:React-Router (https://react-guide.github.io/react-router-cn/),Dva-router (https://dvajs.com/api/#dva-router)... 當我們切換路由時,沒有被匹配到的?Component?也會被整體替換掉,原有的狀態也丟失了。因此,當用戶從詳情頁退回到列表頁時,會重新加載列表頁面組件,重新走一遍生命周期,獲取的就是第一頁的數據,從而回到了列表頂部,下面是常用的路由匹配代碼段。

function?RouterConfig({?history,?app?})?{const?routerData?=?getRouterData(app);return?(<ConnectedRouter?history={history}><Routepath="/"render={(props)?=>?<Layouts?routerData={routerData}?{...props}?/>}redirectPath="/exception/403"/></ConnectedRouter>);
}
//?路由配置說明(你不用加載整個配置,
//?只需加載一個你想要的根路由,
//?也可以延遲加載這個配置)。
React.render((<Router><Route?path="/"?component={App}><Route?path="about"?component={About}/><Route?path="users"?component={Users}><Route?path="/user/:userId"?component={User}/></Route><Route?path="*"?component={NoMatch}/></Route></Router>
),?document.body)

如何解決

原因找到了,那么我們怎么去緩存頁面或者數據呢?一般有兩種解決方式:1. 路由切換時自動保存狀態。2. 手動保存狀態。在?Vue?中,可以直接使用?keep-alive?來實現組件緩存,只要使用了?keep-alive?標簽包裹的組件,在頁面切換的時候會自動緩存?失活?的組件,使用起來非常方便,簡單例子如下。

<!--?失活的組件將會被緩存!-->
<keep-alive><component?v-bind:is="currentTabComponent"></component>
</keep-alive>

但是,React?中并沒有?keep-alive?這種類似的標簽或功能,官方認為這個功能容易造成內存泄漏,暫不考慮支持 (https://github.com/facebook/react/issues/12039)。

所以只能是在路由層做手腳,在路由切換時做對應的緩存操作,之前有開發者提出了一種方案:通過樣式來控制組件的顯示/隱藏 (https://github.com/facebook/react/issues/12039),但是這可能會有問題,例如切換組件的時候無法使用動畫,或者使用?ReduxMobx?這樣的數據流管理工具,還有開發者通過?React.createPortal?API?實現了?React?版本的?React Keep Alive?(https://github.com/Sam618/react-keep-alive),并且使用起來也比較方便。第二種解決方案就是手動保存狀態,即在頁面卸載時手動將頁面的狀態收集存儲起來,在頁面掛載的時候進行數據恢復,個人采用的就是簡單粗暴的后者,實現上比較簡單。緩存緩存,無外乎就是兩件事,存和取,那么在存、取的過程中需要注意哪些問題呢?

個人認為需要注意的有以下幾點:

存什么?何時存?存在哪?何時取?在哪取?

存什么

首先我們需要關心的是:存什么?既然要緩存,那么我們要存的是什么?是緩存整個?Component、列表數據還是滾動容器的?scrollTop。舉個例子,微信公眾號里的文章就做了緩存,任意點擊一篇文章瀏覽,瀏覽到一半后關閉退出,再一次打開該文章時會停留在之前的位置,而且大家可以自行測試一下,再次打開的時候文章數據是重新獲取的,在這種場景下,是緩存了文章詳情滾動容器的滾動高度,在離開頁面的時候存起來,再次進入的時候拿到數據后跳轉到之前的高度,除此之外,還有很多別的緩存的方式,可以緩存整個頁面,緩存?state?的數據等等,這些都可以達到我們想要的效果,具體用哪一種要看具體的業務場景。

何時存

其次,我們需要考慮的是什么時候存,頁面跳轉時會有多種?action?導航操作,比如:POPPUSHREPLACE?等,當我們結合一些比較通用的路由庫時,action?會區分的更加細致,對于不同的?action?在不同的業務場景下處理的方式也不盡相同。還是拿微信公眾號舉例,文章詳情頁面就是無腦存,無論是?PUSHPOP?都會存高度數據,所以我們無論跳轉多少次頁面,再次打開總能跳轉到之前離開時的位置,對于商品列表的場景時,就不能無腦存了,因為從?List?->?Detail?->?List?需要緩存沒問題,但是用戶從?List?返回到其他頁面后再次進入?List?時,是進入一個新的頁面,從邏輯上來說就不應該在用之前緩存的數據,而是重新獲取數據。正確的方式應該是進行?PUSH?操作的時候存,POP?的時候取。

存在哪

  1. 持久化緩存。如果是數據持久化可存到?URL?或?localStorage?中,放到?URL?上有一個很好點在于確定性,易于傳播。但?URL?可以先?pass?掉,因為在復雜列表的情況下,需要存的數據比較多,全部放到?URL?是不現實的,即使可以,也會讓?URL?顯得極其冗長,顯然不妥。localStorage?是一種方式,提供的?getItemsetItem?等 api 也足夠支持存取操作,最大支持 5M,容量也夠,通過序列化?Serialize?整合也可以滿足需求,另外?IndexDB?也不失為一種好的方式,WebSQL?已廢棄,就不考慮了,詳細可點擊張鑫旭的這篇文章《HTML5 indexedDB前端本地存儲數據庫實例教程》(https://www.zhangxinxu.com/wordpress/2017/07/html5-indexeddb-js-example/)查看對比。

  2. 內存。對于不需要做持久化的列表或數據來說,放內存可能是一個更好的方式,如果進行頻繁的讀寫操作,放內存中操作 I/O 速度快,方便。因此,可以放到 Redux?或?Rematch?等狀態管理工具中,封裝一些通用的存取方法,很方便,對于一般的單頁應用來說,還可以放到全局的?window?中。

何時取

在進入緩存頁面的時候取,取的時候又有幾種情況

  1. 當導航操作為?POP?時取,因為每當?PUSH?時,都算是進入一個新的頁面,這種情況是不應該用緩存數據。

  2. 無論哪種導航操作都進行取數據,這種情況需要和何時存一起看待。

  3. 看具體的業務場景,來判斷取的時機。

在哪取

這個問題很簡單,存在哪就從哪里取。

CacheHoc?的方案

  • 存什么:列表數據 + 滾動容器的滾動高度

  • 何時存:頁面離開且導航操作為?PUSH

  • 存在哪:window

  • 何時取:頁面初始化階段且導航操作為?POP?的時候

  • 在哪取:window

CacheHoc?是一個高階組件,緩存數據統一存到?window?內,通過?CACHE_STORAGE?收斂,外部僅需要傳入?CACHE_NAMEscrollElRefs?即可,CACHE_NAME?相當于緩存數據的?key,而?scrollElRefs?則是一個包含滾動容器的數組,為啥用數組呢,是考慮到頁面多個滾動容器的情況,在?componentWillUnmount?生命周期函數中記錄對應滾動容器的?scrollTopstate,在?constructor?內初始化?state,在?componentDidMount?中更新?scrollTop

簡單使用

import?React?from?'react'
import?{?connect?}?from?'react-redux'
import?cacheHoc?from?'utils/cache_hoc'@connect(mapStateToProps,?mapDispatch)
@cacheHoc
export?default?class?extends?React.Component?{constructor?(...props)?{super(...props)this.props.withRef(this)}//?設置?CACHE_NAMECACHE_NAME?=?`customerList${this.props.index}`;scrollDom?=?nullstate?=?{orderBy:?'2',loading:?false,num:?1,dataSource:?[],keyWord:?undefined}componentDidMount?()?{//?設置滾動容器listthis.scrollElRefs?=?[this.scrollDom]//?請求數據,更新?state}render?()?{const?{?history?}?=?this.propsconst?{?dataSource,?orderBy,?loading?}?=?this.statereturn?(<div?className={gcmc('wrapper')}><MeScrollclassName={gcmc('wrapper')}getMs={ref?=>?(this.scrollDom?=?ref)}loadMore={this.fetchData}refresh={this.refresh}up={{page:?{num:?1,?//?當前頁碼,默認0,回調之前會加1,即callback(page)會從1開始size:?15?//?每頁數據的數量//?time:?null?//?加載第一頁數據服務器返回的時間;?防止用戶翻頁時,后臺新增了數據從而導致下一頁數據重復;}}}down={{?auto:?false?}}>{loading???(<div?className={gcmc('loading-wrapper')}><Loading?/></div>)?:?(dataSource.map(item?=>?(<Cardkey={item.clienteleId}data={item}{...this.props}onClick={()?=>history.push('/detail/id')}/>)))}</MeScroll><div?className={styles['sort']}><div?className={styles['sort-wrapper']}?onClick={this._toSort}><span style={{?marginRight:?3?}}>最近下單時間</span><imgsrc={orderBy?===?'2'???SORT_UP?:?SORT_DOWN}alt='sort'style={{?width:?10,?height:?16?}}/></div></div></div>)}
}

效果如下:

緩存的數據:

代碼

const?storeName?=?'CACHE_STORAGE'
window[storeName]?=?{}export?default?Comp?=>?{return?class?CacheWrapper?extends?Comp?{constructor?(props)?{super(props)//?初始化if?(!window[storeName][this.CACHE_NAME])?{window[storeName][this.CACHE_NAME]?=?{}}const?{?history:?{?action?}?=?{}?}?=?props//?取?stateif?(action?===?'POP')?{const?{?state?=?{}?}?=?window[storeName][this.CACHE_NAME]this.state?=?{...state,}}}async?componentDidMount?()?{if?(super.componentDidMount)?{await?super.componentDidMount()}const?{?history:?{?action?}?=?{}?}?=?this.propsif?(action?!==?'POP')?returnconst?{?scrollTops?=?[]?}?=?window[storeName][this.CACHE_NAME]const?{?scrollElRefs?=?[]?}?=?this//?取?scrollTopscrollElRefs.forEach((el,?index)?=>?{if?(el?&&?el.scrollTop?!==?undefined)?{el.scrollTop?=?scrollTops[index]}})}componentWillUnmount?()?{const?{?history:?{?action?}?=?{}?}?=?this.propsif?(super.componentWillUnmount)?{super.componentWillUnmount()}if?(action?===?'PUSH')?{const?scrollTops?=?[]const?{?scrollElRefs?=?[]?}?=?thisscrollElRefs.forEach(ref?=>?{if?(ref?&&?ref.scrollTop?!==?undefined)?{scrollTops.push(ref.scrollTop)}})window[storeName][this.CACHE_NAME]?=?{state:?{...this.state},scrollTops}}if?(action?===?'POP')?{window[storeName][this.CACHE_NAME]?=?{}}}}
}

總結

以上的?CacheHoc?只是最簡單的一種實現,還有很多可以改進的地方,譬如:直接存在?window?中有點粗暴,多頁應用下存到?window?會丟失數據,可以考慮存到?IndexDB?或者?localStorage?中,另外這種方案若不配合上?mescroll?需要在?componentDidMount?判斷?state?內的數據,若有值就不初始化數據,這算是一個?bug

緩存方案縱有多種,但需要考慮的問題就以上幾點。另外在講述需要注意的五個點的時候,著重介紹了存什么和存在哪,其實存在哪不太重要,也不需要太關心,找個合適的地方存著就行,比較重要的是存什么、何時存,需要結合實際的應用場景,來選擇合適的方式,可能不同的頁面采用的方式都不同,沒有固定的方案,重要的是分析存取的時機和位置。


最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你進群。


·················?若川出品?·················

今日話題

還有最后一天上班就放五一小長假啦(努力讓內心喜悅不被發現),雖然扣除2天補班,?2天周末,實際天只有1天假期~哈哈,但是能連著休息5天,也還是很不錯哦。趁著小長假可以好好放休息休息,整理一下之前沒及時整理的東西,大家五一都有什么計劃呢?歡迎在下方留言~? 歡迎分享、收藏、點贊、在看我的公眾號文章~

一個愿景是幫助5年內前端人走向前列的公眾號

可加我個人微信?ruochuan12,長期交流學習

推薦閱讀

我在阿里招前端,我該怎么幫你?(現在還能加我進模擬面試群)

若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?

點擊方卡片關注我、加個星標,或者查看源碼等系列文章。
學習源碼整體架構系列、年度總結、JS基礎系列

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

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

相關文章

SQL未能排它地鎖定數據庫以執行該操作解決

SQL未能排它地鎖定數據庫以執行該操作解決&#xff1a; --原因其他用戶或進程在用著數據庫 /* 關閉用戶打開的進程處理 */ use master if exists (select * from dbo.sysobjects where id object_id(N[dbo].[p_killspid]) and OBJECTPROPERTY…

不只是coding_不只是外表

不只是coding“We just need it to look more professional…”“我們只需要看起來更專業...” “We don’t have the graphic expertise you do…”“我們沒有您所需要的圖形專業知識……” “I just don’t know how to make it look good…”“我只是不知道如何使它看起來…

讀取 wps_軟件前世今生篇之WPS(求伯君1988年先于OFFICE研發出WPS)

軟件前世今生篇之WPS今天給大家普及一下WPS這款辦公軟件&#xff0c;相信你會問wps有什么可普及的&#xff1f;我們都知道啊&#xff0c;不就是一款辦公軟件&#xff0c;而且還是抄襲office的&#xff0c;安裝還挺簡單的&#xff0c;而且還有一大堆廣告&#xff0c;不過使用免費…

吳恩達機器學習筆記11-梯度下降法實踐2-學習率

梯度下降算法收斂所需要的迭代次數根據模型的不同而不同&#xff0c;我們不能提前預知&#xff0c;我們可以繪制迭代次數和代價函數的圖表來觀測算法在何時趨于收斂。 也有一些自動測試是否收斂的方法&#xff0c;例如將代價函數的變化值與某個閥值&#xff08;例如0.001&#…

制作五彩紙屑轉場動效_何時以及如何將五彩紙屑添加到產品UI

制作五彩紙屑轉場動效As I am sure all designers have picked up on, confetti has become a popular method of (positive) feedback inside mobile and desktop apps. I will discuss the viable scenarios where you can implement confetti and will even provide some co…

【無套路送書】架構師是怎樣煉成的?

大家好&#xff0c;我是若川。不知道這是今年第幾次送書了&#xff0c;前三次分別是&#xff1a;第一次&#xff0c;第二次&#xff0c;第三次。本次《架構師的自我修煉》&#xff0c;非常珍貴&#xff0c;我爭取到了2本送給大家&#xff0c;送書規則見文末。可以參與下&#x…

WinForm中使用Excel控件

&#xfeff;最近項目中要在WinForm中使用Excel控件&#xff0c;經過幾天的研究&#xff0c;現在總結一下成果。 在WinForm中使用Excel控件主要有三種方法&#xff1a;WebBrowser、DSOFramer、OWC。下面分別描述一下如何使用。 一、WebBrowser /// -1、如何使用 WebBrowser 控件…

python腳本自動化盲注_三、基于報錯型注入和sql盲注的自動化實現

通過前面payload的構造&#xff0c;不難發現&#xff0c;對于報錯型注入和布爾注入(sql盲注)純手工注入的效率是非常慢的。這些payload語句雖然復雜&#xff0c;但大部分內容都是相同的&#xff0c;因此&#xff0c;一言不合就寫了個腳本自動化注入&#xff0c;坐等信息爆出的感…

NASA公布“門戶計劃”,在月球軌道建立空間站進一步探索月球

門戶是NASA研發一種小型的宇宙飛船的名字&#xff0c;該宇宙飛船將圍繞月球軌道運行 成為宇航員臨時住所和辦公室。 日前&#xff0c;美國宇航局&#xff08;以下簡稱“NASA”&#xff09;公布了“門戶計劃”&#xff0c;該計劃具體是指在月球軌道上建立空間站&#xff0c;以幫…

淺析Page.LoadTemplate(模板)方法動態獲取綁定模板后,通過FindControl獲取服務端控件的方法。...

平常使用DataList數據控件綁定數據時&#xff0c;都是在ItemTemplate項里面放入 <asp:DataList ID"list2"runat"server"><ItemTemplate><asp:HyperLink ID"hl"runat"server"></asp:HyperLink></ItemTempl…

蘋果5s變磚_蘋果磚的故事以及可以改進的地方

蘋果5s變磚Even since I can remember I’ve always been curious about trying out all kinds of software, checking out different operating systems, and improving my own user experience through customizing them. Over the years I’ve had the opportunity to test …

學習 launch-editor 源碼整體架構,探究 vue-devtools「在編輯器中打開組件」功能實現原理...

1. 前言你好&#xff0c;我是若川[1]&#xff0c;微信搜索「若川視野」關注我&#xff0c;專注前端技術分享&#xff0c;一個愿景是幫助5年內前端開闊視野走向前列的公眾號。歡迎加我微信ruochuan12&#xff0c;長期交流學習。這是學習源碼整體架構系列 之 launch-editor 源碼&…

:傳遞給 left 或 substring 函數的長度參數無效。_Java函數式編碼結構-好程序員

好程序員Java培訓分享Java函數式編碼結構&#xff0c;本文將探討三種下一代JVM語言&#xff1a;Groovy、Scala和Clojure&#xff0c;比較并對比新的功能和范例&#xff0c;讓Java開發人員對自己近期的未來發展有大體的認識&#xff0c;下面我們一起來看一下吧。當垃圾回收成為主…

系統架構師學習筆記_第十一章(上)_連載

第十一章 信息安全技術 11.1 信息安全關鍵技術 11.1.1 加密和解密 有意的計算機犯罪 和 無意的數據破壞 被動攻擊&#xff1a;非法地從傳輸信道上截取信息&#xff0c;或從存儲載體上 偷竊、復制 信息。 主動攻擊&#xff1a;對傳輸或存儲的數據進行 惡意的刪除、篡改 等。 …

跨庫一致性_設計跨平臺的一致性

跨庫一致性I offended an Apple employee the other day when I was checking out the new iPad Pro and I told him that I was an Android phone user. Eyes rolled, jokes were made, and we agreed to disagree.前幾天&#xff0c;我在檢閱新iPad Pro時冒犯了一名蘋果員工&…

React-生命周期雜記

前言 自從React發布Fiber之后&#xff0c;更新速度日新月異&#xff0c;而生命周期也隨之改變&#xff0c;雖然原有的一些生命周期函數面臨廢棄&#xff0c;但理解其背后更新的機制也是一種學習 在這里根據官方文檔以及社區上其他優秀的文章進行一個對于生命周期的總結&#xf…

漫畫 | 一個NB互聯網項目的上線過程…

大家好&#xff0c;我是若川&#xff08;點這里加我微信 ruochuan12&#xff0c;長期交流學習&#xff09;。今天雖然是周六&#xff0c;但還是要上班&#xff0c;所以就推薦一篇比較輕松的漫畫。點擊下方卡片關注我、加個星標&#xff0c;或者查看源碼等系列文章。學習源碼整體…

stm32 中斷處理級別_STM32中斷優先級徹底講解

文章來源&#xff1a;http://blog.sina.com.cn/s/blog_4fed55ce0100j7nd.html一&#xff1a;綜述STM32 目前支持的中斷共為 84 個(16 個內核68 個外部)&#xff0c; 16 級可編程中斷優先級的設置(僅使用中斷優先級設置 8bit 中的高 4 位)和16個搶占優先級(因為搶占優先級最多可…

胖子臉:庫珀·布萊克100年

In 16th century Europe, roman typefaces were the first to surpass blackletter as the preferred choice for expressing emphasis in print. True bold weight roman letters didn’t appear until the 19th century, which critics quickly coined “Fat Faces” due to …

C語言中的布爾值

C語言的布爾類型在C語言標準(C89)沒有定義布爾類型&#xff0c;所以C語言判斷真假時以0為假&#xff0c;非0為真。所以我們通常使用邏輯變量的做法&#xff1a; //定義一個int類型變量&#xff0c;當變量值為0時表示false&#xff0c;值為1時表示trueint flag;flag 0;//......…