说起来ajax,相信诸位都不会陌生 —— 这可是当下前端最火的技术之一。
ajax请求数据,将数据拿到前端页面上,通过一定手段展示给用户,造成“不必刷新页面”的局部数据刷新。这是ajax最主要的功能。
一直以来使用ajax都是“横冲直撞”:不管三七二十一,调用就完了。拿到数据后也是直接放到页面区域上
——一般来说没有什么问题,直到遇见了分页:一个大数据量重复请求的场景。
于是,我们对ajax的使用做了优化:
(本文所说皆基于“切换分页”场景)
fg
ajax分页缓存
这个可是个“明星人物”。它基于这样一个背景:当你点击某一页时,网站会想后台发一个请求,接收到返回数据后跳过去(局部刷新)。这时,如果没有设置缓存,那么原来页面的数据就不会被浏览器“记住”,当你返回这个页面时浏览器会再一次发送请求,甚至当你中途退出后再进来,又回到原来的下标了。这大大加重了浏览器和服务器的负担!
对如下场景:
<div class="wrap flex_column">
<div class="content flex_column"></div>
<div class="page">
<ul class="flex_row">
<!-- <li></li> --> <!-- 这里实际是后端动态渲染列表 -->
</ul>
</div>
</div>
即是要缓存,我们在获取元素时就要设置【缓存对象】:
var oContent=document.querySelector('.content');
var oPages=document.querySelector('.page ul li');
var cache={};
changePage(); //运行“检查数据”函数
检查数据函数的代码如下:
function changePage(){
for(let i=0,len=oPages.length;i<len;i++){ //这里实际要换成从后端ajax过来的长度
if(oPages[i] in cache){
console.log('已经存在了数据');
//...
}else{
console.log('数据还没有,正在加载中...');
//...
}
console.log(cache);
}
}
可以看到,我们在函数中先进行判断:当前列表项是否已经在缓存中:如果在缓存中了,那我们就不必再发送ajax请求数据 —— 那样会增加服务器的压力,而是直接从缓存中拿到数据:
在上面代码if中,增加:addDom(cache[i]);
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;
}
反之,我们就要发送ajax从服务器拿到数据:
在else中增加代码 goTo(page);
function goTo(page){
Ajax({
url:'xxxx',
method:'GET',
data:{
//...
page
},
success:function(res){
var result=JSON.parse(res);
var dataList=result.showapi_res_body.newslist;
//先获取到我们的数据数组
addDom(dataList);
cache[page]=dataList;
}
});
}
这个思路在“切换分页”这个场景下用的非常多 —— 每次切换时都要将当前页的数据存到缓存中,每次切换时都先去看一下要去的“上一页”或“下一页”的数据是否在缓存中(之前是否切换到此也过),做不同的处理。
fg使用H5的history改善Ajax列表请求
既然HTML5的众多API都是基于浏览器原生的,那么我们从原生的角度来看一下请求数据加载:
当前在点击“下一页”时,大部分网页都采用了动态请求的方式,避免页面刷新。虽然大家都是ajax,但是从一些小的细节还是可以看出其优劣:比如是否支持浏览器“后退”和“前进”键(左上角的两个按钮)。
数据分页显示,早起的做法是在网址后面加个page的参数:在点击“下一页”时,让网页重定向到page+1的新地址。例如之前新浪的新闻网就是这么做的。但是这个列表并不是页面的主体部分!或者说页面的其他部分也有很多图片等丰富的元素,例如导航slider —— 再使用这样的方式,整个页面会闪烁的很厉害,并且很多资源得重新渲染。
但是话说回来,普通的动态请求不会使网址发生变化。用户点击了下一页,或者点击了第几页,想返回到上一个页面时,可能去点浏览器的返回键,这样就导致了返回的不是原先查看的页面信息,而是上一个网址了,或者想刷新一下,发现回到了第一页。
下面以笔者所做的一个案例进行分析:
var pageIndex=0;
function makePage(pageIndex){
var request=new XMLHttpRequest();
request.open("GET","/getBook?page="+pageIndex+"&limit=8",true);
request.send(null);
request.onreadystatechange=statechange;
function statechange(){
if(this.readyState == 4 && this.status == 200){
var books=JSON.parse(request.responseText);
renderPage(books); //渲染数据
}
}
}
拿到数据后进行渲染:
function renderPage(books){
var bookHtml=`<table>`;
for(let i in books){
bookHtml+=`<tr><td>${books[i].name}</td><td>${books[i].author}</td></tr>`;
}
bookHtml+=`</table>`;
bookHtml+=`<button>上一页</button>
<button onclick="nextPage()">下一页</button>`;
var section=document.createElement("section");
section.innerHTML=bookHtml;
document.getElementById("book").appendChild(section);
}
这样一个基本的ajax请求就搭起来了,然后就是我们分析的重点:“下一页”按钮:
function nextPage(){
pageIndex++;
makeRequest(pageIndex);
}
到此,如果不做任何处理的话,就不能够发挥浏览器返回、前进按钮的作用。
如果能够检测到用户点了后退、前进按钮的话,就可以做些文章:HTML5就有这么一个事件 window.onpopstate
,当用户单击前进后退按钮就会触发这个事件 —— 但是光这样还不够,还得传些参数,因为这个事件只能检测到你手动添加上去的url,而且我们返回到之前那个页面时得知道那个页面的pageIndex。HTML5里也有这样一个函数pushState:
window.history.pushState(state,title,url);
其中state是一个object,为当前页面的数据。title没有什么用。url为当前页面的url —— 一旦更改了这个url,浏览器地址栏的地址也会跟着变化(但页面不会刷新)。
还有就是页面load事件未触发之前,点击后退也不会触发popstate事件
于是我们这样做:
function nextPage(){
pageIndex++;
makeRequest(pageIndex);
window.history.pushState({page:pageIndex},null,window.location.href);
}
然后去监听popstate事件:
window.addEventListener("popstate",function(event){
var page=0;
//state数据通过event传进来,这样就可以得到pageIndex
if(event.state !== null){
page=event.state.page;
}
makeRequest(page);
pageIndex=page;
})
这样就会发现,按钮被激活了。
到这里就基本完工了。但是我们发现:在第二页点击刷新的话,首先会出现第一页,点击下一页,出现第二页。然后点返回按钮,发现还是第二页,再次点击时才会回到第一页。
打开控制台,会发现:点第一次返回时获取到的pageIndex仍然是1,即第二页。
我们可以理解为:对history,浏览器有一个队列,用来存放访问的记录,包括每个访问的网址还有state数据。一开始打开页面,队列的首指针指向page=0的位置,点下一页时,执行了pushState,在这个队列插入了一个元素,队首指针移向了page=1的位置。同时通过pushState操作记录了这个元素的url和state数据。
从这里可以看出,pushState最重要的作用还是给history队列插入元素,这样浏览器的后退按钮才不是置灰的状态,其次才是上面所说的存放数据。
那么当前我们最重要的就是“及时保存(缓存)数据 & 更换pageIndex”:
我们整合一下所用到的函数:
var pageIndex=window.localStorage.pageIndex || 0;
function nextPage(){
window.localStorage,pageIndex=++pageIndex;
makeRequest(pageIndex);
window.history.pushState({page:pageIndex},null,window.location.href);
}
window.addEventListener("popstate",function(event){
var page=0;
//state数据通过event传进来,这样就可以得到pageIndex
if(event.state !== null){
page=event.state.page;
}
makeRequest(page);
window.localStorage,pageIndex=page;
})
在改变pageIndex的同时,将其放到localStorage里,这样刷新页面时就可以获取到当前页的pageIndex。
还有一种方法是将其放到url参数里:通过改变当前网址。pageIndex从网址里面取:
var pageData=window.location.search.match(/page=([^&#]+)/);
var pageIndex=pageData ? +pageData[1] : 0;
function nextPage(){
++pageIndex;
makeRequest(pageIndex);
window.history.pushState({page:pageIndex},null,"?page="+pageIndex);
}
这样的好处在于,链接会跟着变,带着pageIndex参数的链接无论是前端渲染还是服务端渲染都可以实现。并且分享给别人时,打开页面也会直接看到同步的数据(比如跳转到第几页的数据页面)。
使用localStorage,唯一担心的不过就是用户开启了隐身/无痕模式(对localStorage的禁用)。
本文笔者原发于csdn: https://yunxiaomeng.blog.csdn.net/article/details/106747078
转载请注明出处!
网友评论