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)

发布了195 篇原创文章 · 获赞 391 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_43624878/article/details/104043831