懒加载其实就是延迟加载,是一种对网页性能优化可方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
我们都知道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
声明:文章著作权归作者所有,如有侵权,请联系小编删除。
于一些拥有大量图片的网站来说,如果一个页面有超过 50 张图片,就会造成网站页面加载太慢以及移动端耗费流量太大。就像前几天给理想做的作品页面,页面上至少200张图,为了解决这样的问题,可以使用LazyLoad按需加载,又称懒加载。
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
什么是LazyLoad按需加载
LazyLoad按需加载采用图片按需加载技术,打开页面时只会加载首屏图片。访客往下滚动时才会陆续加载需要展现的图片,这样非常高效,体验舒适。
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
LazyLoad按需加载实现方法
我们在自己做网站时,也可以实现LazyLoad按需加载,增强网站的用户体验。下面学做网站论坛就来介绍一下LazyLoad按需加载实现方法。(以下会使用到HTML代码,如果对代码不熟悉,可以学习一下html视频教程)
引入LazyLoad按需加载必须的二个文件:jquery.js和jquery.lazyload.js。引入方法很简单,只需将下面的代码放到</head>标签上方即可;
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script src="https://raw.githubusercontent.com/tuupola/jquery_lazyload/master/jquery.lazyload.min.js"></script>
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
网站上所有图片都使用以下的格式书写:
<img class="lazy" src="" data-original="图片地址" width="100" height="100" alt="">
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
在网站的</body>标签上面,放上以下的JS代码,来实现LazyLoad按需加载(懒加载);
<script type="text/javascript">$(function() { $("img.lazy").lazyload({ threshold : 200, // 设置阀值 effect : "fadeIn" // 设置图片渐入特效 });});</script>
做个笔记,图片如何实现懒加载(LazyLoad按需加载)
这样,我们自己在建网站时,也可以轻松实现LazyLoad按需加载(懒加载)了。快去自己的网站上试试吧!
网站典型负载中,图像和视频是非常重要的一部分内容。 不过遗憾的是,项目干系人并不愿意从其现有应用中删除任何媒体资源。 这种僵局不免令人沮丧,尤其是当所有相关方都希望改善网站性能,但却无法就具体方法达成一致时。 幸运的是,延迟加载解决方案可以减少初始页面负载_和_加载时间,但不会删减任何内容。
什么是延迟加载?
延迟加载是一种在加载页面时,延迟加载非关键资源的方法, 而这些非关键资源则在需要时才进行加载。 就图像而言,“非关键”通常是指“屏幕外”。 如果您曾使用过 Lighthouse 并检验过其提供的改进机会,就有可能从 屏幕外图像审核中看到一些这方面的指导:
您可能已经见过延迟加载的实际应用,其过程大致如下:
您可以在热门发布平台 Medium 上找到图像延迟加载的示例。该平台在加载页面时会先加载轻量级的占位符图像,并在其滚动到视口时,将之替换为延迟加载的图像。
图像延迟加载实际应用示例。 占位符图像在页面加载时加载(左侧),当滚动到视口时,最终图像随即加载(即在需要时加载)。
为何要延迟加载图像或视频,而不是直接_加载_?
因为直接加载可能会加载用户永远不会查看的内容, 进而导致一些问题:
延迟加载图像
内联图像
<img> 元素中使用的图像是最常见的延迟加载对象。 延迟加载 <img> 元素时,我们使用 JavaScript 来检查其是否在视口中。 如果元素在视口中,则其 src(有时是 srcset)属性中就会填充所需图像内容的网址。
使用 Intersection Observer
如果您曾经编写过延迟加载代码,您可能是使用 scroll 或 resize 等事件处理程序来完成任务。 虽然这种方法在各浏览器之间的兼容性最好,但是现代浏览器支持通过 Intersection Observer API 来检查元素的可见性,这种方式的性能和效率更好。
注:并非所有浏览器都支持 Intersection Observer。 如果浏览器之间的兼容性至关重要,请务必阅读下一节,其中会说明如何使用性能稍差(但兼容性更好!)的 scroll 和 resize 事件处理程序来延迟加载图像。
与依赖于各种事件处理程序的代码相比,Intersection Observer 更容易使用和阅读。这是因为开发者只需要注册一个 Observer 即可监视元素,而无需编写冗长的元素可见性检测代码。 然后,开发者只需要决定元素可见时需要做什么即可。 假设我们的延迟加载
元素采用以下基本标记模式:
<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
在此标记中,我们应关注三个相关部分:
现在,我们来看看如何在 JavaScript 中使用 Intersection Observer,并通过以下标记模式延迟加载图像:
document.addEventListener("DOMContentLoaded", function() { var lazyImages=[].slice.call(document.querySelectorAll("img.lazy")); if ("IntersectionObserver" in window) { let lazyImageObserver=new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { let lazyImage=entry.target; lazyImage.src=lazyImage.dataset.src; lazyImage.srcset=lazyImage.dataset.srcset; lazyImage.classList.remove("lazy"); lazyImageObserver.unobserve(lazyImage); } }); }); lazyImages.forEach(function(lazyImage) { lazyImageObserver.observe(lazyImage); }); } else { // Possibly fall back to a more compatible method here } });
在文档的 DOMContentLoaded 事件中,此脚本会查询 DOM,以获取类属性为 lazy 的所有 <img> 元素。 如果 Intersection Observer 可用,我们会创建一个新的 Observer,以在 img.lazy 元素进入视口时运行回调。 请参阅此 CodePen 示例,查看代码的实际运行情况.
注:此代码使用名为 isIntersecting 的 Intersection Observer 方法,该方法在 Edge 15 的 Intersection Observer 实现中不可用。 因此,以上延迟加载代码(以及其他类似的代码片段)将会失败。 请查阅此 GitHub 问题,以获取有关更完整的功能检测条件的指导。
不过,虽然Intersection Observer在浏览器之间获得良好的支持,但并非所有浏览器皆提供支持。 对于不支持 Intersection Observer 的浏览器,您可以使用 polyfill,或者如以上代码所述,检测 Intersection Observer 是否可用,并在其不可用时回退到兼容性更好的旧方法。
使用事件处理程序(兼容性最好的方法)
虽然您_应该_使用 Intersection Observer 来执行延迟加载,但您的应用可能对浏览器的兼容性要求比较严格。 您_可以_使用 polyfil 为不支持 Intersection Observer 的浏览器提供支持(这种方法最简单),但也可以回退到使用 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 示例中,查看代码的实际运行情况。
虽然此代码几乎可在任何浏览器中正常运行,但却存在潜在的性能问题,即重复的 setTimeout 调用可能纯属浪费,即使其中的代码受限制,它们仍会运行。 在此示例中,当文档滚动或窗口调整大小时,不管视口中是否有图像,每 200 毫秒都会运行一次检查。 此外,跟踪尚未延迟加载的元素数量,以及取消绑定滚动事件处理程序的繁琐工作将由开发者来完成。
简而言之:请尽可能使用 Intersection Observer,如果应用有严格的兼容性要求,则回退到事件处理程序。
CSS 中的图像
虽然 <img> 标记是在网页上使用图像的最常见方式,但也可以通过 CSS background-image 属性(以及其他属性)来调用图像。 与加载时不考虑可见性的 <img> 元素不同,CSS 中的图像加载行为是建立在更多的推测之上。 构建文档和 CSS 对象模型以及渲染 树后,浏览器会先检查 CSS 以何种方式应用于文档,再请求外部资源。 如果浏览器确定涉及某外部资源的 CSS 规则不适用于当前构建中的文档,则浏览器不会请求该资源。
这种推测性行为可用来延迟 CSS 中图像的加载,方法是使用 JavaScript 来确定元素在视口内,然后将一个类应用于该元素,以应用调用背景图像的样式。 如此即可在需要时而非初始加载时下载图像。 例如,假定一个元素中包含大型主角背景图片:
<div class="lazy-background"> <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-image 属性,而且我们会在元素进入视口时对其添加这个类:
.lazy-background { background-image: url("hero-placeholder.jpg"); /* Placeholder image */ } .lazy-background.visible { background-image: url("hero.jpg"); /* The final image */ }
我们将从这里使用 JavaScript 来检查该元素是否在视口内(通过 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); }); } });
如上文所述,由于并非所有浏览器都支持 Intersection Observer,因此您需要确保提供回退方案或 polyfill。 请参阅此 CodePen 演示,查看代码的实际运行情况。
延迟加载视频
与图像元素一样,视频也可以延迟加载。 在正常情况下加载视频时,我们使用的是 <video> 元素(尽管也可以改为使用 <img>,不过实现方式受限)。 但是,延迟加载 <video> 的_方式_取决于用例。 下文探讨的几种情况所需的解决方案均不相同。
视频不自动播放
对于需要由用户启动播放的视频(即_不_自动播放的视频),最好指定 <video> 元素的 preload 属性:
<video controls preload="none" poster="one-does-not-simply-placeholder.jpg"> <source src="one-does-not-simply.webm" type="video/webm"> <source src="one-does-not-simply.mp4" type="video/mp4"> </video>
这里,我们使用值为 none 的 preload 属性来阻止浏览器预加载_任何_视频数据。 为占用空间,我们使用 poster 属性为 <video> 元素提供占位符。 这是因为默认的视频加载行为可能会因浏览器不同而有所不同:
由于浏览器在 preload 方面的默认行为并非一成不变,因此您最好明确指定该行为。 在由用户启动播放的情况下,使用 preload="none" 是在所有平台上延迟加载视频的最简单方法。 但 preload 属性并非延迟加载视频内容的唯一方法。 利用视频 预加载快速播放或许能提供一些想法和见解,助您了解如何通过 JavaScript 播放视频。
但遗憾的是,当我们想使用视频代替动画 GIF 时,事实证明以上方法无效。我们将在下文介绍相关内容。
用视频代替动画 GIF
虽然动画 GIF 应用广泛,但其在很多方面的表现均不如视频,尤其是在输出文件大小方面。 动画 GIF 的数据大小可达数兆字节, 而视觉效果相当的视频往往小得多。
使用 <video> 元素代替动画 GIF 并不像使用 <img> 元素那么简单。 动画 GIF 具有以下三种固有行为:
使用 <video> 元素进行替代类似于:
<video autoplay muted loop playsinline> <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> 标记:
<video autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg"> <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 的图像延迟加载示例类似的 JavaScript:
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 属性开始自动播放。
利用这种方法,我们即可提供模拟动画 GIF 行为的视频解决方案。这种方案的流量消耗量低于动画 GIF,而且能延迟加载内容。
延迟加载库
如果您并不关心延迟加载的_实现_细节,只想直接选择使用现有的库(无需感到羞愧!),您有很多选项可以选择。 许多库使用与本指南所示标记模式相似的标记模式。 以下提供一些实用的延迟加载库:
可能出错的地方
虽然延迟加载图像和视频会对性能产生重要的积极影响,但这项任务并不轻松。 如果出错,可能会产生意想不到的后果。 因此,务必要牢记以下几点:
注意首屏
使用 JavaScript 对页面上的所有媒体资源进行延迟加载很诱人,但您必须抵挡住这种诱惑。 首屏上的任何内容皆不可进行延迟加载, 而应将此类资源视为关键资产,进行正常加载。
以正常而非延迟加载方式加载关键媒体资源的主要理据是,延迟加载会将这些资源的加载延迟到 DOM 可交互之后,在脚本完成加载并开始执行时进行。 对于首屏线以下的图像,可以采用延迟加载,但对于首屏上的关键资源,使用标准的 <img> 元素来加载速度会快得多。
当然,如今用来查看网站的屏幕多种多样,且大小各有不同,因此首屏线的具体位置并不明确。 笔记本电脑上位于首屏的内容在移动设备上可能位于首屏线_以下_。 目前并没有完全可靠的建议,无法在每种情况下完美解决这个问题。 您需要清点页面的关键资产,并以典型方式加载这些图像。
此外,您可能也不想严格限定首屏线作为触发延迟加载的阈值。 对您来说,更理想的做法是在首屏线以下的某个位置建立缓冲区,以便在用户将图像滚动到视口之前,即开始加载图像。 例如,Intersection Observer API 允许您在创建新的 IntersectionObserver 实例时,在 options 对象中指定 rootMargin 属性。 如此即可为元素提供缓冲区,以便在元素进入视口之前触发延迟加载行为:
let lazyImageObserver=new IntersectionObserver(function(entries, observer) { // Lazy loading image code goes here }, { rootMargin:"0px 0px 256px 0px" });
如果 rootMargin 的值与您为 CSS margin 属性指定的值相似,这是因为该值就是如此!在本例中,我们将观察元素(默认情况下为浏览器视口,但可以使用 root 属性更改为特定的元素)的下边距加宽 256 个像素。 这意味着,当图像元素距离视口不超过 256 个像素时,回调函数将会执行,即图像会在用户实际看到它之前开始加载。
要使用滚动事件处理代码实现这种效果,只需调整 getBoundingClientRect 检查以包括缓冲区,如此一来,您即可在不支持 Intersection Observer 的浏览器上获得相同效果。
布局移位与占位符
若不使用占位符,延迟加载媒体可能会导致布局移位。 这种变化不仅会让用户产生疑惑,还会触发成本高昂的 DOM 布局操作,进而耗用系统资源,造成卡顿。 您至少应考虑使用纯色占位符来占用尺寸与目标图像相同的空间,或者采用 LQIP 或 SQIP 等方法,在媒体项目加载前提供有关其内容的提示。
对于 <img> 标记,src 最初应指向一个占位符,直到该属性更新为最终图像的网址为止。 请使用 <video> 元素中的 poster 属性来指向占位符图像。 此外,请在 <img> 和 <video> 标记上使用 width 和 height 属性。 如此可以确保从占位符转换为最终图像时,不会在媒体加载期间改变该元素的渲染大小。
图像解码延迟
在 JavaScript 中加载大型图像并将其放入 DOM 可能会占用主线程,进而导致解码期间用户界面出现短时间无响应的情况。 您可以先使用 decode 方法异步解码图像,再将其插入到 DOM 中,以减少此类卡顿现象,但请注意: 这种方法尚不能通用,而且会增加延迟加载逻辑的复杂性。 如果要采用这种方法,请务必进行检查。 以下示例显示如何通过回退来使用 Image.decode()
var newImage=new Image(); newImage.src="my-awesome-image.jpg"; if ("decode" in newImage) { // Fancy decoding logic newImage.decode().then(function() { imageContainer.appendChild(newImage); }); } else { // Regular image load imageContainer.appendChild(newImage); }
请参阅此 CodePen 链接,查看与此示例相似的代码的实际运行情况。 如果您大部分的图像都相当小,则这种方法的帮助不大,但肯定有助于减少延迟加载大型图像并将其插入 DOM 时的卡顿现象。
内容不加载
有时,媒体资源会因为某种原因而加载失败,进而导致发生错误。 何时会发生这种情况?何时发生视情况而定,以下是一种假设情况: 您有一个短时间(例如,5 分钟)的 HTML 缓存策略,而用户访问网站,_或_保持打开旧选项卡并长时间离开(例如,数个小时),然后返回继续阅读内容。 在此过程中的某个时刻,发生重新部署。 在此部署期间,图像资源的名称因为基于哈希的版本控制而更改,或者完全移除。 当用户延迟加载图像时,该资源已不可用,因此导致加载失败。
虽然出现这种情况的机会比较小,但您也有必要制定后备计划,以防延迟加载失败。 对于图像,可采取如下解决方案:
var newImage=new Image(); newImage.src="my-awesome-image.jpg"; newImage.onerror=function(){ // Decide what to do on error }; newImage.onload=function(){ // Load the image };
发生错误时采取何种措施取决于应用。 例如,可以将图像占位符区域替换为按钮,以允许用户尝试重新加载该图像,或者直接在图像占位符区域显示错误消息。
此外,也可能会发生其他情况。 无论采取何种方法,在发生错误时通知用户,并提供可能的解决方案总不是坏事。
JavaScript 可用性
不应假定 JavaScript 始终可用。 如果要延迟加载图像,请考虑提供 <noscript> 标记,以便在 JavaScript 不可用时显示图像。 例如,最简单的回退方法是使用 <noscript> 元素在 JavaScript 处于关闭状态时提供图像:
<!-- An image that eventually gets lazy loaded by JavaScript --> <img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load.jpg" alt="I'm an image!"> <!-- An image that is shown if JavaScript is turned off --> <noscript> <img src="image-to-lazy-load.jpg" alt="I'm an image!"> </noscript>
如果 JavaScript 已关闭,用户会_同时_看到占位符图像以及 <noscript> 元素中包含的图像。 要解决此问题,我们可以在 <html> 标记上放置 no-js 类,如下所示:
<html class="no-js">
然后,在通过 <link> 标记请求任何样式表之前,于 <head> 中放置一行内联脚本,用于在 JavaScript 处于打开状态时从 <html> 元素中移除 no-js 类:
<script>document.documentElement.classList.remove("no-js");</script>
最后,我们可以使用一些 CSS,在 JavaScript 不可用时隐藏类为 lazy 的元素,如下所示:
.no-js .lazy { display: none; }
这并不会阻止占位符图像加载,但是结果却更令人满意。 关闭 JavaScript 的用户不只是能看到占位符图像,这要比只能看到占位符和没有意义的图像内容更好。
结论
务必谨慎使用延迟加载图像和视频方法,该方法可以显著减少网站上的初始加载时间和页面负载。 用户不查看的媒体资源不会为其带来不必要的网络活动和处理成本,但用户可以根据需要查看这些资源。
就性能改进方法而言,延迟加载无可争议。 如果您的网站上存在大量内联图像,这是减少非必要下载的好方法。 您的网站用户和项目干系人都会因该方法而受益匪浅!
*请认真填写需求信息,我们会在24小时内与您取得联系。