使用IntersectionObserver延迟加载图像

如今,高速加载站点路径上的主要绊脚石是图像。 对于电子商务项目尤其如此。 它们上的图像通常很“繁重”,占页面内容的大部分。 通常,这导致以下事实:为了向用户显示页面,其浏览器需要下载几兆字节的图形数据。 在这种情况下如何加快页面加载速度? 这个问题的答案专用于我们今天出版的材料的翻译。

图片

一般规定


例如,考虑一下沃尔玛内政部的首页


包含许多图像的页面

这是有关加载多少图像以形成此页面的信息:


在页面形成过程中加载的图像

如您所见,有137张图片! 这意味着显示页面并通过网络传输的数据有80%以上以图形文件的形式呈现。

现在,我们分析页面加载时执行的网络请求:


在页面形成期间执行的网络请求

在这种情况下,由于项目代码分离而导致的文件下载得比其晚。 这是由于您首先需要加载主捆绑包cp_ny.bundle 。 如果该捆绑包不受18个彼此争夺带宽的图像的干扰,则下载速度可能会更快。

如何解决? 实际上,要真正“解决”此问题是行不通的,但是您可以做很多事情来优化图像加载。 有许多方法可以优化网页上使用的图像。 其中包括使用各种图形文件格式,数据压缩,使用模糊动画技术,使用CDN。 我想谈谈图像的所谓“延迟加载”(延迟加载)。 特别是,我们将讨论如何在React网站上实现该技术,但是由于它基于JavaScript机制,因此可以集成到任何Web项目中。

试点项目


让我们从一个非常简单的Image React组件开始:

 class Image extends PureComponent { render() {   const { src } = this.props;   return <img align="center" src={src} />; } } 

它以URL为属性,并使用它呈现img HTML元素。 这是相关的JSFiddle代码。 下图显示了包含此组件的页面。 请注意,为了查看他显示的图像,您需要滚动页面的内容。


具有显示图像的组件的页面

为了在该组件中实现图像的延迟加载技术,您需要执行以下三个步骤:

  1. 下载后不要立即渲染图像。
  2. 设置用于检测页面内容查看区域中图像外观的工具。
  3. 检测到图像落入查看区域后,显示图像。

让我们看一下这些步骤。

第一步


在此步骤中,不显示刚加载后的图像。

 render() { return <img />; } 

第二步


在这里,我们配置了机制,使我们能够检测图像进入查看区域的时刻。

 componentDidMount() { this.observer = new IntersectionObserver(() => {   //        }, {   root: document.querySelector(".container") }); this.observer.observe(this.element); } .... render() { return <img ref={el => this.element = el} />; } 

让我们分析一下这段代码。 这是这里所做的:

  • ref属性已添加到img元素。 这样,您以后就可以更新src的图像链接,而不必重新渲染组件。
  • IntersectionObserverIntersectionObserver的新实例(我们将在下面讨论)。
  • 提示IntersectionObserver对象使用observe(this.element)构造观察图像。

什么是IntersectionObserver ? 考虑到“交集”一词被翻译为“交集”,“观察者”为“观察者”,人们已经可以猜出该对象的作用。 如果在MDN上查找有关它的信息,则可以发现Intersection Observer API允许Web应用程序异步监视元素与其父元素或视口文档范围的交集中的更改。

乍一看,API的这一特性似乎并不是特别容易理解,但实际上,它的结构非常简单。 IntersectionObserver实例传递了几个参数。 特别是,我们使用了root参数,该参数允许我们针对元素与我们需要知道的边界的交集来设置我们认为是容器的root DOM元素。 默认情况下,这是页面(视口)的可见片段所在的区域,但是我明确地将其设置为使用位于JSFiddle的iframe元素中的容器。 这样做是为了稍后考虑不设计为使用iframe元素的一种可能性。

与更传统的方法(例如一起使用onScrollgetBoundingClientRect()相比,使用IntersectionObserver确定项目何时可见的原因更为普遍,因为IntersectionObserver机制在主线程之外运行。 但是,在IntersectionObserver检测到元素与容器的相交之后调用的回调是在主线程中自然执行的,因此其代码不应太繁琐。

第三步


现在,我们需要配置在检测到target元素(在本例中为this.element )与root容器(在本例中为div元素.container )的交集时调用的回调。

 .... this.observer = new IntersectionObserver( entries => {   entries.forEach(entry => {     const { isIntersecting } = entry;     if (isIntersecting) {       this.element.src = this.props.src;       this.observer = this.observer.disconnect();     }   }); }, {   root: document.querySelector(".container") } ); .... 

当检测到相交时, entries数组将传输到entries ,该entries类似于一组针对所有目标元素的状态快照的快照,为其检测到指定边界的相交。 isIntersecting属性指示相交的方向。 如果要监视的项目不在根元素之外,则为true 。 如果元素离开根元素,则为false

因此,当发现该元素已与容器的下边框交叉时,我手动设置了它的src属性并关闭了对其的监视,这不再是必需的。

步骤4(秘密)


现在,在我们工作的第四个秘密步骤,您可以欣赏结果并享受成功。 这是收集我们刚才讨论的内容的代码


应用惰性图像加载技术的结果

但是,如果您仔细研究一下我们所拥有的东西,事实证明,在这里您会发现不是很好的东西。 为了看到这一点,我快速滚动了页面,同时减慢了网络连接的速度。


快速滚动并降低网络连接速度时的页面行为

由于我们仅在图像到达其应该已经可见的区域后才加载图像,因此用户在加载之前没有机会滚动浏览页面并查看图像所占据的区域,当然还有图像本身。 当站点从连接到快速Internet的普通计算机上查看时,这不会引起问题。 但是许多现代用户都通过手机访问站点,有时他们使用3G网络,甚至更糟的是EDGE连接。

没错,解决这个问题并不那么困难。 之所以可以这样做,是因为Intersection Observer API为开发人员提供了扩展或缩小根元素边界的能力(在我们的例子中,这是.container元素)。 为了利用这个机会,只需在配置根容器的位置添加一行代码:

 rootMargin: "0px 0px 200px 0px" 

rootMargin属性中,编写一行其结构符合用于配置元素缩进的CSS规则的行。 在我们的案例中,我们告知系统,用于检测元素与容器相交的下边界需要增加200像素。 这意味着当元素落入根元素下边框下方200像素的区域(默认值为0)时,将调用相应的回调。

这是实现此技术代码。


改进延迟加载图像的技术

结果,当我们仅将页面滚动到列表的第4个元素时,图像就会加载到页面可见区域下方200像素的区域中。
现在看来,所需的一切都已完成。 但是事实并非如此。

影像高度问题


如果您仔细研究了上面的GIF插图,则可能会注意到在加载图像后,滚动条会出现“跳转”。 幸运的是,这个问题很容易解决。 其原因是,显示图像的元素最初的高度为0,在加载图像后,高度为300像素。 因此,要解决该问题,只需在图像中添加属性height={300} ,将元素设置为固定高度即可。

关于优化结果


页面上应用惰性图像加载后,我们在沃尔玛获得了哪些结果? 实际上,具体结果取决于许多情况,其中可以注意到客户端的网络连接速度,CDN可用性,每页图像数以及用于检测与它们所应用的根元素的交集的规则。 换句话说,对于您来说,为了评估图像的延迟加载对您自己的项目的影响,最好自己实施和验证。 但是,如果您仍然想看看懒惰的图像加载给我们带来了什么,这里有一些Lighthouse报告。 第一个在优化之前形成,第二个在优化之后形成。


优化前生成的Lighthouse报告


优化后生成的Lighthouse报告

总结


今天,我们研究了一种使用惰性图像加载来优化网页的技术。 如果您的站点页面上到处都是图片,那么很有可能该技术对您很有用。

亲爱的读者们! 您如何优化图像及其加载?

Source: https://habr.com/ru/post/zh-CN423485/


All Articles