美文网首页大前端Web前端之路前端开发那些事
无限滚动的优化方案(一):预加载实现

无限滚动的优化方案(一):预加载实现

作者: small_a | 来源:发表于2017-03-04 11:51 被阅读216次

    最近看了很多关于无限滚动的文章,也在面试中被问到了,优化方案很多,本次针对其中一条优化方案做了一个实际的优化:预加载。

    实例简介

    之前一直对单页应用有兴趣,所以自己写了一个前端路由,相关的文章见这里,这个单页应用采取hash的方式实现路由。最终的实例页面见这里。仓库在这里是一个经典的单页应用。要做优化的就是主页的信息滚动。这些信息通过ajax从服务器端获取,这里为了方便,服务器端会一直返回数据,哪怕是重复的。
    页面如下:

    Paste_Image.png

    预加载原理

    正常情况下我的一个ajax请求服务器端会返回最多10条信息,当用户滑动到页面底部,就会触发ajax请求,发送一条新的请求来获取信息。但是这样造成了用户的等待,但是如果用户空闲,就进行ajax的发送也是不行的,这个浪费了用户的流量。
    需要采用一种检测手段,检测用户会不会继续往下看,如果会,就进行预加载,不必等到用户翻到最底部。这样可以最大程度的优化用户的体验

    预加载实现

    剩余内容的高度检测

    理解了原理,其实预加载的核心,就是进行一个即将滚动到底部的一个检测。这里需要用到一些高度,我采用的是

    1. document.body.scrollTop:这个高度是指浏览器滚动的高度
    2. document.body.clientHeight:这个是用户视窗的高度
    3. document.body.offsetHeight:这个是body的高度,也就是整个文档的实际高度。
      这三个高度,前两个加起来就是用户已经翻看的高度,通过与整个文档的高度的对比就能检测到剩余文档的高度,只要这个高度小于阈值(OFFSET),就进行ajax请求的发送,从而实现预加载
    var body = document.body;
    var height = body.offsetHeight-(body.scrollTop+body.clientHeight);
    if(height<OFFSET){
        //浏览器快滚动到底部了
    }
    

    scroll事件反复执行带来的性能问题

    上述函数可以直接和scroll事件进行绑定,但是这样直接绑定会造成一些不好的影响:

    1. scroll的反复执行降低了页面的流畅度
    2. 当达到目标高度之后,每一次的滚动都符合预加载的要求,所以轻微滚动都会触发n次的预加载请求。

    scroll事件优化方案

    针对上述第一个问题,可以采用函数节流的方式进行优化,但是要注意,最后一次用户的滚动必须执行,因为有可能最后一次滚动进入了阈值,此时必须尽快执行预加载请求。关于节流的原理网上描述的比较多,此处给一个我实现的包装代码:

    method.throttling2 = function(func,delay){
      var inthrott = false;//节流
      var timer;//保证最后一次执行
      return function(){
        var self = this;
        var args = arguments;//保留上下文
        if(!inthrott){//判断上一次是否执行完毕
          func.apply(self,args);
          inthrott = true;
          setTimeout(function(){
            inthrott = false;
          },delay);
        }else {
          clearTimeout(timer);
          timer = setTimeout(function(){
            func.apply(self,args);
            inthrott = false;
          },500);//此处500可以进行加快,主要是希望能够尽快的执行最后一次
        }
      }
    }
    

    而要解决第二个问题,就必须要保证上一次ajax请求没有结束之前,不会进行下一次ajax请求。这个其实通过一个标识符就可以解决,默认情况下是true,当ajax请求发送开始,修改为false,此时高度的改变会触发scroll函数,但是函数内部会判断这个标识符,如果为false,就不会进行高度的检测以及下一个ajax请求的发送。而当ajax请求结束后,标志位回归true,从而用户的滚动就可以触发下次的预加载了。

    最后的代码大概像这样:

    //method是我定义的一些公共方法
    method.addevent(window,'scroll',method.throttling2(scrollTop,1000))//绑定scroll函数,通过之前的节流函数对原函数进行包装
    
    //高度检测函数,通过节流之后绑定在了scroll事件上
    function scrollTop(){
      var height1 = document.body.scrollTop+document.body.clientHeight;
      var height2 = document.body.offsetHeight;
      if(state){//检测标识变量
          if(height2 - height1 < eleheight*3){
            console.log('到达预加载阈值,开始预加载');
            getInfo();
          }
      }
    }
    //包装ajax请求,加上标识变量
    function getInfo(){
      //从服务器端获取商家发布的新信息
      state = false;
      //发送实际的ajax请求
        {state = true;}//此语句写入ajax的回调
    }
    

    总结

    目前我在预加载的时候会进行一个输出,方便我的观察以及调试。最后大概用户距离底部300px左右的时候,会触发预加载,此时,用户的任何滚动先经过节流,然后再进行标识的检测,不会出现两次预加载同时出现的情况。达到了我预期的目标。

    展望

    我认为,现实中还有可能出现一种极端情况:就是用户不断的下滑,在查找之前的一条历史消息,此时,因为我的预加载的阻拦,其实在一定程度上限制了用户的滑动(每次只能同时进行一个ajax请求),这里应该有优化的空间,可以通过检测是否出现在可视区域来进行加载。
    另外一个可以优化的点在于图片,如果我的作品里面不是框,而是图片,预加载的效果就不太好,需要图片的懒加载,也就是用户可视范围内才进行图片的加载。还有待后续进行改进

    相关文章

      网友评论

        本文标题:无限滚动的优化方案(一):预加载实现

        本文链接:https://www.haomeiwen.com/subject/rgbdgttx.html