美文网首页工作生活
如何平滑使用原生 Lazy Loading【译】

如何平滑使用原生 Lazy Loading【译】

作者: 宫若石 | 来源:发表于2019-07-02 14:06 被阅读0次

| 导语 在网页资源加载优化的路上,相信大家对 lazy loading 一点不陌生。可喜可贺的是,<img> 和 <iframe> 上将新增了 loading 属性,而且已发布的 Chrome 75 版本支持了该特性。四月份社区刚发布这个消息的时候,当时跟进了下。关于这个话题,之前看过几篇不错的文章,本文主要是对 "Hybrid Lazy Loading: A Progressive Migration To Native Lazy Loading" 的翻译。不是为作者的 vanilla-lazyload 打 call 哈,我之前没用过他这个。

建议阅读:
【1】、Native image lazy-loading for the web

【2】、Hybrid Lazy Loading: A Progressive Migration To Native Lazy Loading

本文翻译的是文章【2】,习惯看英文文档的,建议直接看原文。这里主要是做一下整理和以后碰到类似需求,可以参考下。另外,本人翻译水平有限,难免有翻译不恰当的地方。我尽量表达通顺,没必要翻译的词,适当加下注解。

1、前言

原生 Lazy-Loading 即将到来。然而,它不像我们之前实现 Lazy-Loading 的方式,它的处理是革命性的,因为其不依赖于 JavaScript,而且能让我们用起来更方便。但是问题来了,这个特性我们没法 polyfill(一段代码或者插件, 提供了那些开发者们希望浏览器原生提供支持的功能),而且要得到所有浏览器厂商的支持,还将有一段时间。通过这篇文章,你将了解到原生 Lazy-Loading 是怎么工作的,并且能了解到如何逐步选择性地取代之前 JavaScript 驱动下的 Lazy-Loading。感谢 Hybrid(混合模式) Lazy Loading。
前几周,你可能已经听说了原生 Lazy-Loading 将在几个月后的 Chromium 75 版本里支持(75 版本已经上线了)。
“耶,好消息,但我们必须等到所有浏览器都支持它才行。”
如果这个想法在你脑海中出现了,那继续读这篇文章。我将尝试说服你反其道而行之。让我们开始吧,先对比下原生 Lazy-Loading 和 JavaScript 驱动的方式。

2、Native VS JavaScript-Driven Lazy Loading

Lazy-Loading 是一个提高网站和 Web 应用性能的方式,它通过延迟加载视窗外的内容来最大化加快视窗内的图片和 Iframe(和视频)的渲染速度。

2.1、JAVASCRIPT-DRIVEN LAZY LOADING

为了延迟加载图片或 Iframe,一般常用的处理方式就是通过用类似的数据属性 `data-src` 来替换 `src` 属性,标记好后,然后用 JS 去检测图片或 Iframe 是不是快接近可视区域了(比如往下滚动屏幕的时候),然后去拷贝数据属性 `data-src` 的值到 `src` 上,然后触发那些延迟加载的内容。
<img data-src="turtle.jpg" alt="Lazy turtle" class="lazy">

2.2、NATIVE LAZY LOADING

根据[原生 Lazy-Loading 规范](http://github.com/whatwg/html/pull/3752/),如果你想通过原生的这个特性延迟加载图片或 Iframe 的话,你仅仅只需要添加 `loading=lazy` 属性在相关的标签上即可。
<img src="turtle.jpg" alt="Lazy turtle" loading="lazy">
Addy Osmani 写了一些关于这个主题的文章 “[Native Image Lazy-Loading For The Web!](https://addyosmani.com/blog/lazy-loading/)”,提到了 Google Chrome 团队已经在开发这个特性,并试图加入 Chrome 75 的版本里。
其他基于 Chromium 的浏览器,比如 Opera 和 Microsoft Edge 都将受益于此,在基于 Chromium 75 的更新后,同样会支持这个特性。一起期待吧。

2.2、NATIVE LAZY LOADING

根据[原生 Lazy-Loading 规范](http://github.com/whatwg/html/pull/3752/),如果你想通过原生的这个特性延迟加载图片或 Iframe 的话,你仅仅只需要添加 `loading=lazy` 属性在相关的标签上即可。
<img src="turtle.jpg" alt="Lazy turtle" loading="lazy">
Addy Osmani 写了一些关于这个主题的文章 “[Native Image Lazy-Loading For The Web!](https://addyosmani.com/blog/lazy-loading/)”,提到了 Google Chrome 团队已经在开发这个特性,并试图加入 Chrome 75 的版本里。
其他基于 Chromium 的浏览器,比如 Opera 和 Microsoft Edge 都将受益于此,在基于 Chromium 75 的更新后,同样会支持这个特性。一起期待吧。
2.2.1、Get Started With Native Lazy Loading
为了防止你网站所有的图片,在页面打开的时候被一次性全部加载完,你可以尝试在你网站上开启(支持的话)原生的 Lazy-Loading,通过简单地添加一个 HTML 属性即可。`loading` 属性会告诉浏览器,哪些图片非常重要,需要立即加载,哪些可以在页面滚动时再延迟加载。同样,`loading` 属性也可以加在 Iframe 上。
为了告诉浏览器哪些图片非常重要,需要立即加载,你必须添加 `loading="eager"` 属性在 `img` 标签上。最好是把这个图片放到最开始的可视范围内。
<img src="rabbit.jpg" alt="Fast rabbit" loading="eager">
如果要让浏览器延迟加载某些图片,可以添加 `loading="lazy"` 属性。尤其是这些图片一开始不在可视范围内,建议你这么处理。
<img src="turtle.jpg" alt="Lazy turtle" loading="lazy">
仅仅通过添加 `loading` 属性在你的图片和 Iframe 上,你就可以在你的网站启用原生 Lazy-Loading。你的网站将会慢慢受益于它,因为以后越来越多浏览器会支持这个特性。
如果你的网站还没有使用任何延迟加载的技术来做一些优化,这个方式对你就最好不过了。但是如果你已经通过 JavaScript 驱动的延迟加载模式在处理了,你可能想保持它,同时逐步切换为原生的 Lazy-Loading 模式。
理想的解决方案,就是立即开始使用原生 Lazy-Loading,同时使用 polyfill 来使得所有的浏览器都可以生效。不幸的是,原生 Lazy-Loading 不是一个我们可以通过 JavaScript 可以 polyfill 的特性。
2.2.2、No Use For A Polyfill
当一个新的浏览器技术在某一个单独的浏览器发布后,开源社区通常都会发布一个 JavaScript Polyfill 给其他的浏览器,以此提供同样的支持。比如 `[IntersectionObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver)` polyfill,使用 JavaScript 和 DOM 元素,再结合下 `Element.getBoudingClientRect()`,即可实现其原生的 API 功能。
原生 Lazy-Loading 不同的是,因为一个 `loading="lazy"` 的 JavaScript polyfill,需要尽可能快地阻止浏览器加载这些标记了属性的内容。JavaScript 没法控制页面初始渲染阶段,因此原生 Lazy-Loading 的 polyfill 基本不可能实现了。

3、Hybrid Lazy Loading

如果你不满足于使用原生 Lazy-Loading 只是作为一种渐进式增强模式,或者你已经实现了基于 JS 的 Lazy-Loading,而且不想在少数浏览器上失去这个特性(同时又希望能在支持了原生 Lazy-Loading 的浏览器上启用原生的),那么你就需要一个不同的解决方案。下面介绍下 Hybrid Lazy Loading。
Hybrid Lazy Loading,顾名思义,如果浏览器支持原生 Lazy-Loading 的话,则使用原生的,否则,用 JS 来实现 Lazy-Loading
为了实现混合模式的 Lazy-Loading,你需要使用 `data` 属性来标记你需要延迟加载的内容(和 JS 驱动的 Lazy-Loading 一样),同时添加 `loading="lazy"` 属性。
<img data-src="turtle.jpg" loading="lazy" alt="Lazy turtle">
然后你需要写一些 JS. 在页面开始加载的时候,你需要检测浏览器是否支持原生的 Lazy-Loading。然后对标记了 `loading="lazy"` 属性的所有元素进行如下操作:
  1. 如果支持原生的 Lazy-Loading,拷贝 data-src 属性的值到 src 属性上;

  2. 如果不支持,初始化一个 JS Lazy-Loading 的脚本或插件,当这些元素进入可视区域的时候;

    你自己实现这些逻辑并不难。你通过如下条件判断,即可检测浏览器是否支持原生 Lazy-Loading:

if ('loading' in HTMLImageElement.prototype)
如果支持,把 `data-src` 的值拷贝到 `src` 上即可。如果不支持,就初始化你需要的 Lazy-Loading 脚本。

如下是处理这个的代码片段:

<!-- In-viewport images should be loaded normally, or eagerly -->
<img src="important.jpg" loading="eager" alt="Important image">

<!-- Let’s lazy-load the rest of these images -->
<img data-src="lazy1.jpg" loading="lazy" alt="Lazy image 1">
<img data-src="lazy2.jpg" loading="lazy" alt="Lazy image 2">
<img data-src="lazy3.jpg" loading="lazy" alt="Lazy image 3">

<script>
  (function() {
    if ("loading" in HTMLImageElement.prototype) {
      var lazyEls = document.querySelectorAll("[loading=lazy]");
      lazyEls.forEach(function(lazyEl) {
        lazyEl.setAttribute(
          "src",
          lazyEl.getAttribute("data-src")
        );
      });
    } else {
      // Dynamically include a lazy loading library of your choice
      // Here including vanilla-lazyload
      var script = document.createElement("script");
      script.async = true;
      script.src =
        "https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.0.0/dist/lazyload.min.js";
      window.lazyLoadOptions = {
        elements_selector: "[loading=lazy]"
        //eventually more options here
      };
      document.body.appendChild(script);
    }
  })();
</script>
你可以访问下这个[线上Demo](https://www.andreaverlicchi.eu/lazyload/demos/native_lazyload_conditional_old.html),顺便查看下网页源码。

不过这只是一个非常基础的脚本,很多时候我们需要做一些响应式的图片可能会添加其他的属性或标签(比如 `srcset` 和 `sizes` 属性,甚者 `picture` 和 `source` 等标签)。

3.1、A Little Third-Party Help

过去四年里,我一直在维护一个关于 Lazy-Loading 的开源项目,叫“vanilla-lazyload”,在 Addy Osmani 写了关于原生 Lazy-Loading 的文章后,社区反馈说,我的脚本能否作为对应的 polyfill。
我在前面解释过,你没法为原生 Lazy-Loading 这个特性实现一个 polyfill。不过,我还是想到了一个解决方案,可以让开发者们更容易地开始过度到原生 Lazy-Loading, 不需要写任何 JS 代码,我之前提到过的。
从 `vanilla-lazyload` 版本 12 开始,你可以仅仅设置 `use_native` 选项为 `true` 来开启混合模式的 Lazy-Loading。这个脚本经过 gzip 压缩后,只有 2.0 KB 大小了,而且已经可以使用了,在 [GitHub](https://github.com/verlok/lazyload),[npm](https://www.npmjs.com/package/vanilla-lazyload) 和 [jsDelivr](https://www.jsdelivr.com/package/npm/vanilla-lazyload) 上。

4、Demos

你现在可以通过下载 Chrome Canary (当然,你现在直接升级 Chrome 到 75 版本即可使用了)或 Microsoft Edge Insider 后,使用原生的 Lazy-Loading 特性。启用 "Enable lazy image loading" 和 "Enable lazy frame loading" (例如打开 chrome://flags,搜索下 "lazy" 后,设置即可)。

4.1、NATIVE LAZY LOADING DEMO

为了分析下原生的 Lazy-Loading 怎么工作的,你可以玩下这个 Demo。在这个 Demo 中,没有使用任何 JS 代码。有且仅使用了原生的 Lazy-Loading。
*   [测试原生 Lazy-Loading Demo](https://www.andreaverlicchi.eu/lazyload/demos/native_lazyload.html)
**验证**:尝试在不同的浏览器打开,看下有什么不同的表现。支持原生 Lazy-Loading 的浏览器,应该和之前的 Demo 表现一样。在不支持的浏览器上,图片将会在你滚动时就全部加载了。
需要指出的是 `vanilla-lazyload` 使用了 IntersectionObserver API,因为你需要在 IE 和老版本的 Safari 下 polyfill。不过如果没有提供对应的 polyfill 的话也问题不大,因为在这个例子中,`vanilla-lazyload` 将一次性把所有图片下载完。
 **提示**: 建议阅读一下  `vanilla-lazyload` 的 README 文件的 “[To Polyfill Or Not To Polyfill](https://github.com/verlok/lazyload/blob/master/README.md#to-polyfill-or-not-to-polyfill-intersectionobserver)” 章节。

5、Try Hybrid Lazy Loading In Your Website

既然原生的 Lazy-Loading 即将在一些浏览器上支持,为什么你不现在使用下混合模式的 Lazy-Loading 呢?我们开始吧:

5.1、HTML MARKUP

最简单的图片标记,一般就是两个属性:`src` 和 `alt`。
在屏幕内的图片,你应该保留 `src` 属性,同时加一个 `loading="eager"` 属性。
  <img src="important.jpg" loading="eager" alt="Important image">
在屏幕下的一些图片,你应该将 `src` 属性用 `data-src` 来替换,同时添加 `loading="lazy"` 属性。
  <img data-src="lazy.jpg" loading="lazy" alt="A lazy image">
如果你想使用一些响应式的图片,保持这两个 `srcset` 和 `sizes` 属性即可,无需特殊处理。
<img alt="A lazy image" 
    loading="lazy" 
    data-src="lazy.jpg" 
    data-srcset="lazy_400.jpg 400w, lazy_800.jpg 800w" 
    data-sizes="100w">
如果你更喜欢用 `picture` 标签,改一下 `srcset`,`sizes` 和 `src` 属性,在 `source` 标签上。
<picture>
    <source 
        media="(min-width: 1200px)" 
        data-srcset="lazy_1200.jpg 1x, lazy_2400.jpg 2x">
    <source 
        media="(min-width: 800px)" 
        data-srcset="lazy_800.jpg 1x, lazy_1600.jpg 2x">
    <img alt="A lazy image" 
        loading="lazy" 
        data-src="lazy.jpg">
</picture>
`picture`标签也能被用在需要选择加载 WebP 格式的图片上。
**提示**:如果你想知道更多 `vanilla-lazyload` 的用法,请读一下 README 文件 "[Getting Started](https://github.com/verlok/lazyload/blob/master/README.md#-getting-started---html)" 的 HTML 部分。

5.2、JAVASCRIPT CODE

首先,你需要在你的网站里引入 `vanilla-lazyload`。
你可以加载它的 CDN 文件,如 jsDelivr 上的:
<script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.0.0/dist/lazyload.min.js"></script>

或者安装一下对应的 npm 包:

npm install vanilla-lazyload@12
更多引入 `vanilla-lazy` 的方式,可以看 README 文件的 "[Getting Started](https://github.com/verlok/lazyload/blob/master/README.md#-getting-started---script)" 的 JS 部分。
然后,在你的网站或 Web 应用中,你需要写如下的一些代码:
var pageLazyLoad = new LazyLoad({
    elements_selector: "[loading=lazy]",
    use_native: true // ← enables hybrid lazy loading
});
**提示**:`vanilla-lazyload ` 支持很多其他自定义的设置,例如:增加滚动区域的距离,到了对应位置才开始加载元素;或者仅仅当元素在给定时间内在视窗内才加载等。更多的配置,可以看 README 文档中的 [API 部分](https://github.com/verlok/lazyload/blob/master/README.md#-api)。

5.3、ALL TOGETHER, USING AN async SCRIPT

将这些全部放一起,并且使用 `async` (这是一个脚本加载和执行优先级的属性,async 模式执行的优先级会较高,[更多说明可以看这篇文章](https://addyosmani.com/blog/script-priorities/))的方式来加载脚本,是性能最好的。比如参考入下代码:
<!-- In-viewport images should be loaded normally, or eagerly -->
<img src="important.jpg" loading="eager" alt="Important image">

<!-- Let’s lazy-load the rest of these images -->
<img data-src="lazy1.jpg" loading="lazy" alt="Lazy image 1">
<img data-src="lazy2.jpg" loading="lazy" alt="Lazy image 2">
<img data-src="lazy3.jpg" loading="lazy" alt="Lazy image 3">

<!-- Set the options for the global instance of vanilla-lazyload -->
<script>
  window.lazyLoadOptions = {
    elements_selector: "[loading=lazy]",
    use_native: true // ← enables hybrid lazy loading
  };
</script>

<!-- Include vanilla lazyload 12 through an async script -->
<script async src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.0.0/dist/lazyload.min.js"></script>
就这样了!就这些非常简单和容易的步骤,你将可以在你的网站中启用混合模式的 Lazy-Loading。

6、Important Best Practices

  • 仅仅对图片使用 Lazy-Loading 技术,你应该知道,是有可能会加载非视窗内的图片的。建议主动去优先加载视窗内的东西,才能有效提升性能。如果你只是将网站上所有的图片都设置为 Lazy-Loading,这样甚至还会降低渲染性能。
  • 使用 CSS 来预留一些你即将加载的图片的空间。用这个方式,会向下推动其他需要展示的内容。如果你不这么做,很多图片将在首屏立即触发加载。如果你需要这个 CSS 技巧来处理这个事情,你可以在 vanilla-lazyload 的文档中找到对应的技巧。详情请点击

7、Pros And Cons

如下对三种 Lazy-Loading 模式做下优缺点对比:
优点 缺点
原生

  1. 不需要 JS;

  2. 不需要麻烦的设置,直接生效;

  3. 不需要使用 CSS 技巧来为图片预留空间;

  4. 不兼容所有浏览器;

  5. 初始的有效负荷较高,因为需要为每一张图片预加载 2KB.

JS 驱动

  1. 所有浏览器都兼容;

  2. 你能做任何个性化的 UI 技巧,比如模糊化或者延迟加载(一点点加载出来)等;

  3. 它依赖 JS
    混合模式

  4. 它支持是否开启原生的 Lazy-Loading,如果浏览器支持的话;

  5. 所有浏览器都兼容;

  6. 你可以直接移除脚本依赖,只要原生的 Lazy-Loading 被广泛支持了的话

  7. 它同样依赖 JS

8、Wrapping Up

 我非常地激动,原生的 Lazy-Loading 能到来。甚至希望所有的浏览器都能立即实现它。
 同时,你可以选择丰富 HTML 标记来进行渐进增强,且在支持原生 Lazy-Loading 的浏览器上能直接使用到该特性。或者,你可以先混合着使用 Lazy-Loading 技术,直到大量的浏览器都支持了该特性。
不妨试试。另外,别忘了给 GitHub 上的 `[vanilla-lazyload](https://github.com/verlok/lazyload)` 点赞和关注,同时可以评论让我了解你们的想法。

相关文章

网友评论

    本文标题:如何平滑使用原生 Lazy Loading【译】

    本文链接:https://www.haomeiwen.com/subject/tsyzcctx.html