JS設計模式在前端中的應用:緩存的魅力

要說誰在網站、web APP等前端應用中起到越來越重要的作用,那絕對很多人第一時間想到的就是:【緩存】!

面對越來越要求高質量、快應用的前端,緩存越來越成爲當之無愧的“無冕之王”!與其相關的,一直活躍在前端“視野”中的有兩個非常重要的應用:

  1. 【享元模式】中的對象池
  2. Ajax分頁中的信息緩存

請諸位隨筆者一探究竟:


筆者曾經提到過 Java 中 的 String 對象池,下面就來學習這種共享的技術:對象池維護一個裝載空閒對象的池子,如果需要對象的時候,不是直接 new,而是轉從對象池裏獲取。如果對象池裏沒有空閒對象,則創建一個新的對象,當獲取出的對象完成它的職責之後, 再進入池子等待被下次獲取。

對象池的原理很好理解,比如我們組人手一本《JavaScript權威指南》,從節約的角度來講,這並不是很划算,因爲大部分時間這些書都被閒置在各自的書架上,所以我們一開始就只買一本,或者一起建立一個小型圖書館(對象池),需要看書的時候就從圖書館裏借,看完了之後再把書還回圖書館。如果同時有三個人要看這本書,而現在圖書館裏只有兩本,那我們再馬上去書店買一本放入圖書館。

對象池技術的應用非常廣泛,HTTP 連接池和數據庫連接池都是其代表應用。在 Web 前端開發中,對象池使用最多的場景大概就是跟 DOM 有關的操作。很多空間和時間都消耗在了 DOM節點上,如何避免頻繁地創建和刪除 DOM 節點就成了一個有意義的話題。

比如:我們經常會在百度地圖的頁面上看到一個一個的小氣泡,網上將它們稱之爲“toolTip”,我們也暫且這麼稱呼。它們的作用是標記地址。
在地圖上搜索我家附近的時候,頁面裏出現了 2 個小氣泡。當我再搜索附近的漯河高中時,頁面中出現了 3個小氣泡。按照對象池的思想,在第二次搜索開始之前,並不會把第一次創建的2 個小氣泡刪除掉,而是把它們放進對象池。這樣在第二次的搜索結果頁面裏,我們只需要再創建 1 個小氣泡而不是 3 個。。。

通用對象池的實現:

var objectPoolFactory=function(createObjFn){
	var objectPool=[];   //toolTip對象池
	
	return {
		create:function(){
			var obj=objectPool.length===0 ? createObjFn.apply(this,arguments) : objectPool.shift();
			return obj;
		},
		recover:function(obj){
			objectPool.push(obj);   //對象池回收dom
		}
	}
};

//利用objectPoolFactory來裝載一些iframe的對象:
var iframeFactory=objectPoolFactory(function(){
	var iframe=document.createElement('iframe');
	document.body.appendChild(iframe);
	
	iframe.onload=function(){
		iframe.onload=null;   //防止iframe重複加載的bug
		iframeFactory.recover(iframe);   //iframe加載完成後回收節點
	}
});

使用如:

for(var i=0,src,iframeN;src=['http:// baidu.com','http:// QQ.com','http:// 163.com'][i++],iframeN=i;){
	var iframeN=iframeFactory.create();
	iframeN.src=src;
}

對象池是另外一種性能優化方案,它跟享元模式有一些相似之處,但沒有分離內部狀態和外部狀態這個過程。本章用享元模式完成了一個文件上傳的程序,其實也可以用對象池+事件委託來代替實現。


下面該說說著名的“Ajax分頁緩存”了,近兩年這個可是個“明星人物”。它基於這樣一個背景:當你點擊某一頁時,網站會想後臺發一個請求,接收到返回數據後跳過去(局部刷新)。這時,如果沒有設置緩存,那麼原來頁面的數據就不會被瀏覽器“記住”,當你返回這個頁面時瀏覽器會再一次發送請求,甚至當你中途退出後再進來,又回到原來的下標了。這大大加重了瀏覽器和服務器的負擔!

實現【分頁緩存】的方式有不少種,這裏說比較簡單的一種:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>Ajax分頁緩存·試驗</title>
		<style>
			/* ... */
		</style>
	</head>
	<body>
		<div class="wrap flex_column">
			<div class="content flex_column"></div>
			<div class="page">
				<ul class="flex_row">
					<!-- <li></li> -->
				</ul>
			</div>
		</div>
		<script src="./ajax.js"></script>
		<script>
			var oContent=document.querySelector('.content');
			var oPages=document.querySelector('.page ul li');
			
			var cache={};
			changePage();
			setInterval(function(){
				cache={};
			},1000);
			function changePage(){
				for(let i=0,len=oPages.length;i<len;i++){  //這裏實際要換成從後端ajax過來的長度
					oPages[i].onclick=function(){
						var page=i+1;
						if(page in cache){
							addDom(cache[page]);
							console.log('已經存在了數據');
						}else{
							goTo(page);
							console.log('數據還沒有,正在加載中...');
						}
						console.log(cache);
					}
				}
			}
			goTo(1);
			function goTo(page){
				Ajax({
					url:'https://route.showapi.com/181-1',
					method:'GET',
					data:{
						showapi_appid:'30603',
						showapi_sign:'98960666afeb4992ae91971d13494090',
						num:8,
						page:page
					},
					success:function(res){
						var result=JSON.parse(res);
						var dataList=result.showapi_res_body.newslist;
						//先獲取到我們的數據數組
						addDom(dataList);
						cache[page]=dataList;
					}
				});
			}
			function addDom(result){
				var dataList=result;
				var dataLength=dataList.length;
				var str='';
				for(var i=0;i<dataLength;i++){
					str+=`
						<a href="${dataList[i].url}" class="items flex_row">
							<div class="img">
								<img src="xxx" alt="" />
							</div>
							<div class="bd">
								<p class="label">${dataList[i].title}</p>
							</div>
						</a>`
				}
				oContent.innerHTML=str;
			}
		</script>
	</body>
</html>

第二個代碼中筆者使用的ajax模塊是自己封裝的(筆者提倡能自己封裝一下,這樣感觸更深一些),已總結成文,鏈接如下:
原生JS封裝ajax方法(支持jsonp)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章