延迟加载
延迟加载是一种在加载页面时,延迟加载非关键资源的一种方法,而这些非关键资源则在需要时才进行加载,就图像而言,‘非关键’通常指的是‘屏幕外’。
- 延迟加载图像和视频时,可以减少初始页面加载时间、初始页面负载以及系统资源使用量,所有这一切都会对性能产生积极影响。
- 通常来说加载网页时,浏览器会请求所有的图像,而不管它们实在视窗还是在页面的更深处并且不可见。延迟加载允许我们加载可见的图像,并且在用户滚动时按需异步加载其他的图片。这减少了负载请求的数据,并可以大大加快该过程。
延迟加载图像
HTML中的延迟加载内联图像
<img>
元素中使用的图像时最常见的延迟加载对象。延迟加载<img>
元素时,使用javascript来检查其是否在视口中,如果元素在视口中,则其src(有时srcset)属性中就会填充所需图像内容的网址。使用 Intersection Observer。 之前写过的延迟加载代码,有的是使用scroll或resize等时间处理程序来完成任务。这种方式的有点就是各浏览器之间的兼容性最好。但现在浏览器支持通过
Intersection Observer API
来检查元素的可见性。这中方式的性能和效率更好。当然并非所有的浏览器都支持Intersection Observer
。
与依赖于各种事件处理程序的代码相比,Intersection Observer
更容易使用和阅读。只需要注册一个Observer即可监听视元素,就不需要编写冗余的元素可见性检测代码。只需要决定元素可见时需要做什么即可。
<img src alt="I'm an image!">
需要关注的三部分:
- class,javascript中选择元素时需要使用的类选择器。
- src,引用页面最初加载时显示的占位符图片。
- data-src和data-srcset,属于占位属性,其中包含元素进入视口后要加载的图片的网址。
接下来使用Intersection Observer
实现延迟加载图片。
document.addEventListener('DOMContentLoaded',function(){
const lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
}else{
// 对不支持Intersection Observer 其他处理方式
}
})
在DOMContentLoaded
事件中,脚本会查询DOM来获取类属性为lazy
的所有<img>
元素。
Intersection Observer
来做延迟加载,但是对兼容性要求严格,可以使用polyfill来做兼容处理。但是也可以使用scroll
和resize
的代码,getBoundingClientRect
配合使用的orientationchange
事件处理程序,来确定元素是否在视口中。
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
let active = false;
const lazyLoad = function() {
if (active === false) {
active = true;
setTimeout(function() {
lazyImages.forEach(function(lazyImage) {
if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImages = lazyImages.filter(function(image) {
return image !== lazyImage;
});
if (lazyImages.length === 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad);
}
}
});
active = false;
}, 200);
}
};
document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
});
此代码在 scroll 事件处理程序中使用 getBoundingClientRect 来检查是否有任何 img.lazy 元素处于视口中。 使用 setTimeout 调用来延迟处理,active 变量则包含处理状态,用于限制函数调用。 延迟加载图像时,这些元素随即从元素数组中移除。 当元素数组的 length 达到 0 时,滚动事件处理程序代码随即移除。 您可在此 CodePen 示例中,查看代码的实际运行情况。
css中的图像
将通过设置css的background-color属性了调用图片,预加载时不考虑可见性的<img>
不同,css中的图片加载行为是建立在更多推测之上。构建文档和CSSOM以及渲染树后,浏览器会先检查CSS以何种方式适应于文档,再请求外部资源。吐过浏览器确定设计某外部资源成的CSS规则不适用于当前构建的文档,则浏览器将不会请求该资源。
这种推测性行为来延迟CSS中的图片加载,方法是使用Javascript来确定元素在视口内,然后将一个类应用于该元素,已应用背景图像的样式。
<div>
<h1>Here's a hero heading to get your attention!</h1>
<p>Here's hero copy to convince you to buy a thing!</p>
<a href="/buy-a-thing">Buy a thing!</a>
</div>
div.lazy-background
元素通常包含有某些CSS调用的大型背景图片。在延迟加载中,可以通过visible来隔离div.lazy-background
元素的background-color
属性。当元素进入视口时再对其添加这个类。
.lazy-background {
background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}
.lazy-background.visible {
background-image: url("hero.jpg"); /* The final image */
}
通过元素是否在视口内(Intersection Observer
),如果在视口内,则对 div.lazy-background
元素添加visible
类加载图像;
document.addEventListener("DOMContentLoaded", function() {
var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));
if ("IntersectionObserver" in window) {
let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
lazyBackgroundObserver.unobserve(entry.target);
}
});
});
lazyBackgrounds.forEach(function(lazyBackground) {
lazyBackgroundObserver.observe(lazyBackground);
});
}
});
延迟加载视频
在正常情况下加载视屏时,我们使用的是<video>
元素。
视频不需要自动播放
使用度量none的preload
属性来阻止浏览器预加载任何视频数据。poster
属性提供占位符。
<video controls preload="none">
<source src="one-does-not-simply.webm" type="video/webm">
<source src="one-does-not-simply.mp4" type="video/mp4">
</video>
视频代替GIF
虽然动画GIF应用广泛,但其在很多方面的表现均不如视频,尤其是在输出文件大小方面。动画GIF的数据大小可达数兆字节,而视觉效果相当的视频往往很小。
gif图片加载时会自动播放,并且会循环播放而且没有声音。
使用 <video>
元素进行替代类似于:
<video autoplay loop>
<source src="one-does-not-simply.webm" type="video/webm">
<source src="one-does-not-simply.mp4" type="video/mp4">
</video>
autoplay、muted 和 loop 属性的含义不言而喻,而 playsinline 是在 iOS 中进行自动播放所必需。可以跨平台使用的“视频即 GIF”替代方式。Chrome 会自动延迟加载视频,但并不是所有浏览器都会提供这种优化行为。
<video autoplay loop width="610" height="254">
<source data-src="one-does-not-simply.webm" type="video/webm">
<source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>
添加了 poster 属性,您可以使用该属性指定占位符以占用 <video>
元素的空间,直到延迟加载视频为止。 与上文中的 <img>
延迟加载示例一样,我们将视频网址存放在每个 <source> 元素的 data-src 属性中。 然后将使用与上文基于 Intersection Observer 的图像延迟加载示例类似。
document.addEventListener("DOMContentLoaded", function() {
var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));
if ("IntersectionObserver" in window) {
var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(video) {
if (video.isIntersecting) {
for (var source in video.target.children) {
var videoSource = video.target.children[source];
if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
videoSource.src = videoSource.dataset.src;
}
}
video.target.load();
video.target.classList.remove("lazy");
lazyVideoObserver.unobserve(video.target);
}
});
});
lazyVideos.forEach(function(lazyVideo) {
lazyVideoObserver.observe(lazyVideo);
});
}
});
延迟加载 <video>
元素时,我们需要对所有的 <source>
子元素进行迭代,并将其 data-src 属性更改为 src 属性。 完成该操作后,必须通过调用该元素的 load 方法触发视频加载,然后该媒体就会根据 autoplay 属性开始自动播放。
延迟加载库
欢迎访问主页,有更多文章内容
转载请注明原出处
原文链接地址:网页性能优化:延迟加载资源