【干货】设计高性能无限滚动加载,了解高效页面秘密
性能测量
同时,也可以使用performance.mark()标记各种时间戳(就像在地图上打点),保存为各种测量值(测量地图上的点之间的距离),便可以批量地分析这些数据了。
整体思路和方案设计
滚动问题
用户体验优化小窍门
总结一下
。
代码实现
<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; } }
性能收益
继续思考
DOM回收
墓碑(Tombstones)
滚动锚定
总结
颜海镜
原文地址: https://mp.weixin.qq.com/s/8NoLuPddKimfaiLQbYheBQ
最后更新:2017-06-14 16:31:36