閱讀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】