为 `src` 赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。
`preview.png`是预览图片,比较小,加载很快,而且很多图片都共用这个`preview.png`,加载一次即可。
待页面下拉,图片显示出来时,再去替换`src`为`data-realsrc`的值。
<img src="preview.png" data-realsrc="abc.png"/>
这里为何要用`data-`开头的属性值?
所有 HTML 中自定义的属性,都应该用`data-`开头,因为`data-`开头的属性浏览器渲染的时候会忽略掉,提高渲染性能。
懒加载其实就是延迟加载,是一种对网页性能优化可方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
我们都知道HTML中的 <img>标签是代表文档中的一个图像。。说了个废话。。
<img>标签有一个属性是 src,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有 src属性,就不会发送请求。
嗯?貌似这点可以利用一下?
我先不设置 src,需要的时候再设置?
nice,就是这样。
我们先不给 <img>设置 src,把图片真正的URL放在另一个属性 data-src中,在需要的时候也就是图片进入可视区域的之前,将URL取出放到 src中。
<div class="container">
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img1.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img2.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img3.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img4.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img5.png">
</div>
</div>
仔细观察一下, <img>标签此时是没有 src属性的,只有 alt和 data-src属性。
alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过 HTMLElement.dataset来访问。
方法一
网上看到好多这种方法,稍微记录一下。
然后判断②-③<①是否成立,如果成立,元素就在可视区域内。
方法二(推荐)
通过 getBoundingClientRect()方法来获取元素的大小以及位置,MDN上是这样描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为 ClientRect的 DOMRect对象,包含了 top、 right、 botton、 left、 width、 height这些值。
MDN上有这样一张图:
可以看出返回的元素位置是相对于左上角而言的,而不是边距。
我们思考一下,什么情况下图片进入可视区域。
假设 constbound=el.getBoundingClientRect();来表示图片到可视区域顶部距离; 并设 constclientHeight=window.innerHeight;来表示可视区域的高度。
随着滚动条的向下滚动, bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当 bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。
我们这样判断:
function isInSight(el) {
const bound=el.getBoundingClientRect();
const clientHeight=window.innerHeight;
//如果只考虑向下滚动加载
//const clientWidth=window.innerWeight;
return bound.top <=clientHeight + 100;
}
这里有个+100是为了提前加载。
页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。
function checkImgs() {
const imgs=document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el=> {
if (isInSight(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
function throttle(fn, mustRun=500) {
const timer=null;
let previous=null;
return function() {
const now=new Date();
const context=this;
const args=arguments;
if (!previous){
previous=now;
}
const remaining=now - previous;
if (mustRun && remaining >=mustRun) {
fn.apply(context, args);
previous=now;
}
}
}
这里的 mustRun就是调用函数的时间间隔,无论多么频繁的调用 fn,只有 remaining>=mustRun时 fn才能被执行。
可以看出此时仅仅是加载了img1和img2,其它的img都没发送请求,看看此时的浏览器
第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
当我向下滚动,此时浏览器是这样
此时第二张图片完全显示了,而第三张图片显示了一点点,这时候我们看看请求情况
img3的请求发出来,而后面的请求还是没发出~
当滚动条滚到最底下时,全部请求都应该是发出的,如图
方法三 IntersectionObserver
经大佬提醒,发现了这个方法
先附上链接:
jjc大大:
https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver可以自动观察元素是否在视口内。
var io=new IntersectionObserver(callback, option);
// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
callback的参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:
属性描述time可见性发生变化的时间,单位为毫秒rootBounds与getBoundingClientRect()方法的返回值一样boundingClientRect目标元素的矩形区域的信息intersectionRect目标元素与视口(或根元素)的交叉区域的信息intersectionRatio目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0target被观察的目标元素,是一个 DOM 节点对象
我们需要用到 intersectionRatio来判断是否在可视区域内,当 intersectionRatio>0&&intersectionRatio<=1即在可视区域内。
代码
function checkImgs() {
const imgs=Array.from(document.querySelectorAll(".my-photo"));
imgs.forEach(item=> io.observe(item));
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
const io=new IntersectionObserver(ioes=> {
ioes.forEach(ioe=> {
const el=ioe.target;
const intersectionRatio=ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <=1) {
loadImg(el);
}
el.onload=el.onerror=()=> io.unobserve(el);
});
});
源自:segmentfault
声明:文章著作权归作者所有,如有侵权,请联系小编删除。
接:https://juejin.im/book/5b936540f265da0a9624b04b/section/5bb6218ee51d450e7762f873
Lazy-Load,翻译过来是“懒加载”。它是针对图片加载时机的优化:在一些图片量比较大的网站(比如电商网站首页,或者团购网站、小游戏首页等),如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象,因为图片真的太多了,一口气处理这么多任务,浏览器做不到啊!
但我们再想,用户真的需要这么多图片吗?不对,用户点开页面的瞬间,呈现给他的只有屏幕的一部分(我们称之为首屏)。只要我们可以在页面打开的时候把首屏的图片资源加载出来,用户就会认为页面是没问题的。至于下面的图片,我们完全可以等用户下拉的瞬间再即时去请求、即时呈现给他。这样一来,性能的压力小了,用户的体验却没有变差——这个延迟加载的过程,就是 Lazy-Load。
现在我们打开掘金首页:
右侧可能会出现的图片,即下图示例:
大家现在以尽可能快的速度,疯狂向下拉动页面。
*请认真填写需求信息,我们会在24小时内与您取得联系。