阅读997 返回首页    go 阿里云 go 技术社区[云栖]


【干货】设计高性能无限滚动加载,了解高效页面秘密






性能测量




同时,也可以使用performance.mark()标记各种时间戳(就像在地图上打点),保存为各种测量值(测量地图上的点之间的距离),便可以批量地分析这些数据了。






整体思路和方案设计


                  13e0819e9e69ccc0a9254c10f76bb26a5c179baf









滚动问题













用户体验优化小窍门



总结一下




代码实现


 


<div  >
      <ul  >
       </ul>
      <div ></div>
   </div>


<#dataList.forEach(function (v) {#>
        <div  >
            <li>
                <a href="<#=v.href#>">
                    <img  src="data:image/gif;base64,R0lGODdhAQABAPAAAP%2F%2F%2FwAAACwAAAAAAQABAEACAkQBADs%3D" 
                    data-src="<#=v.src#>">
                    data-src="<#=v.src#>">
                    </img>
                    <strong><#=v.title#></strong>
                    <span ><#=v.writer#></span>
                    <span ><#=v.succNum#></span>
                </a>
            </li>
        </div>
    <#})#>




样式亮点



.slide .img{       
display: inline-block;        
width: 90px;      
 height: 90px;       
 margin: 0 auto;      
  opacity: 0;      
  -webkit-transition: opacity 0.25s ease-in-out;        
-moz-transition: opacity 0.25s ease-in-out;      
  -o-transition: opacity 0.25s ease-in-out;      
  transition: opacity 0.25s ease-in-out;    }







(function() {       
 var fetching = false; 
        var page = 1;       
 var slideCache = [];       
 var itemMap = {};       
 var lastScrollY = window.pageYOffset;       
 var scrollY = window.pageYOffset;        
var innerHeight;        
var topViewPort;        
var bottomViewPort;       
 function isVisible (id) {         
   // ...判断元素是否在可见区域
        }        
function updateItemCache (node) {       
     // ....更新DOM缓存
        }        function fetchContent () {        
    // ...ajax请求数据
        }        function handleDefer () {      
     // ...懒加载实现
        }        function handleScroll (e, force) {         
   // ...滚动处理程序
        } 
        window.setTimeout(handleScroll, 100);
        fetchContent();
    }());
        fetchContent();
    }());



// 加载中状态锁
    1)var fetching = false;    // 用于加载时发送请求参数,表示第几屏内容,初始为1,以后每请求一次,递增1
    2)var page = 1; 
    // 只缓存最新一次下拉数据生成的DOM节点,即需要插入的dom缓存数组
    3)var slideCache = []; 
    // 用于已经生成的DOM节点储存,存有item的offsetTop,offsetHeight
    4) var slideMap = {}; 
    // pageYOffset设置或返回当前页面相对于窗口显示区左上角的Y位置。
    5)var lastScrollY = window.pageYOffset; var scrollY = window.pageYOffset;    // 浏览器窗口的视口(viewport)高度
    6)var innerHeight;    // isVisible的上下阈值边界
    7) var topViewPort; 
    8) var bottomViewPort;




滚动处理程序handleScroll






       function handleScroll (e, force) { // 如果时间间隔内,没有发生滚动,且并未强制触发加载,则do nothing,再次间隔100毫秒之后

        if (!force && lastScrollY === window.scrollY) {            window.setTimeout(handleScroll, 100);            return;
        }        
        }        else {            // 更新文档滚动位置
            lastScrollY = window.scrollY;
        }
        scrollY = 
        }
        scrollY = window.scrollY;        // 浏览器窗口的视口(viewport)高度赋值
        innerHeight = window.innerHeight;        // 计算isVisible上下阈值
        topViewPort = scrollY - 1000;
        bottomViewPort = scrollY + innerHeight + 
        bottomViewPort = scrollY + innerHeight + 600;        // 判断是否需要加载
        // document.body.offsetHeight;返回当前网页高度 
        if (window.scrollY + innerHeight + 200 > document.body.offsetHeight) {
            fetchContent();
        }        
            fetchContent();
        }        // 实现懒加载
        handleDefer();        window.setTimeout(handleScroll, 100);
    }
    }


拉取数据





缓存对象



  slideCache = [

           {
              id: "s-97r45",
              img: img DOM节点,
              node: 父容器DOM node,类似<div  ></div>,
             src: 图片资源地址
          }, 
           ...
             ]


slideCache由updateItemCache函数更新,主要用于懒加载时的赋值src。这样我们做到“只写入DOM”原则,不需要再从DOM读取。




function handleDefer () { // 时间记录

        console.time('defer');        // 获取dom缓存
        var list = slideCache;        // 对于遍历list里的每一项,都使用一个变量,而不是在循环内部声明。节省内存,把性能高效,做到极致。
        var thisImg;        for (var i = 0, len = list.length; i < len; i++) {
            thisImg = list[i].img; // 这里我们都是从内存中读取,而不用读取DOM节点
            var deferSrc = list[i].src; // 这里我们都是从内存中读取,而不用读取DOM节点
            // 判断元素是否可见
            if (isVisible(list[i].id)) {                // 这个函数是图片onload逻辑
                var handler = function () {                    var node = thisImg;                    var src = deferSrc;                    // 创建一个闭包
                    return function () {
                        node.src = src;
                        node.style.opacity = 1;
                    }
                }                var img = new Image();
                img.onload = handler();
                img.src = list[i].src;
            }
        }        console.timeEnd('defer');
    }

主要思路就是对DOM缓存中的每一项进行循环遍历。在循环中,判断每一项是否已经进入isVisible区域。如果进入isVisible区域,则对当前项进行真实src赋值,并设置opacity为1。




是否在isVisible区域判断



function isVisible (id) {        
                    var offTop;        
                    var offsetHeight;       
                    var data;        
                    var node;    
   // 判断此元素是否已经懒加载正确渲染,分为在屏幕之上(已经懒加载完毕)和屏幕外,已经添加到dom中,但是还未请求图片(懒加载之前)
        if (itemMap[id]) {            // 直接获取offTop,offsetHeight值
            offTop = itemMap[id].offTop;
            offsetHeight = itemMap[id].offsetHeight;
        }        else {            // 设置该节点,并且设置节点属性:node,offTop,offsetHeight
            node = document.getElementById(id);            // offsetHeight是自身元素的高度
            offsetHeight = parseInt(node.offsetHeight);       // 元素的上外缘距离最近采用定位父元素内壁的距离
            offTop = parseInt(node.offsetTop);
        }        if (offTop + offsetHeight > topViewPort && offTop < bottomViewPort) {            return true;
        }        else {            
                          return false;
        }
    }


09f437746a7c6227e176c73437d0baae102098a0


性能收益




继续思考



DOM回收



22a179abc7d7d7af5b2787c6797098de6d501eb5



5732c1e42717a664877052144cc9f7c00845ca41



墓碑(Tombstones)


49f60613a0e8b7b38696900063b1a958a862c15c




59dcc9df1c708586fddf0677da2957fa4acb42aa


滚动锚定



总结







颜海镜

原文地址: https://mp.weixin.qq.com/s/8NoLuPddKimfaiLQbYheBQ


最后更新:2017-06-14 16:31:36

  上一篇:go  25岁Java工程师如何转型学习人工智能?
  下一篇:go  阿里云机器学习技术分享1——图像识别之TensorFlow实现方法【视频+PPT】