在 Vue.js 中使用交叉点观察者 API 生成懒图像组件

图像,就像大多数媒体格式一样,可以非常沉重,需要大量的时间来加载,我们作为Web开发人员被教导尽可能地压缩它们,为视网膜显示器提供2x版本......并且当有意义时懒惰地加载它们。

问题是,在什么时候懒惰地加载图像有意义?好吧,如果图像位于页面顶部,它很可能会从一开始就可见。在这种情况下,懒惰地加载是不必要的。

交叉观察器 API

在过去,在页面上检测某个元素的可见性是困难的,开发人员需要自己实现它或使用库来实现这一目标,往往会出现缓慢的结果和容易出现错误的解决方案。

Intersection Observer API以非常干净和性能的方式解决了这个问题,它提供了可订阅的模型,我们可以观察到当一个元素进入视图端口时被通知。

这里有一个简单的例子:

1const observer = new IntersectionObserver(entries => {
2  const rainbowDiv = entries[0];
3  if (rainbowDiv.isIntersecting) {
4    // Do something cool here
5  }
6});
7
8const rainbowDiv = document.querySelector("#rainbowDiv");
9observer.observe(rainbowDiv);

我们必须创建一个IntersectionObserver的实例,将订阅回调作为其参数,在那里我们检查它是否通过isIntersecting方法与rainbowDiv交叉。

正如您可能已经注意到的那样,订阅回调接收了一系列条目,这意味着您可以观察多个元素:

 1const observer = new IntersectionObserver(entries => {
 2  entries.forEach(entry => {
 3    if (entry.isIntersecting) {
 4      // ...
 5    }
 6  });
 7});
 8
 9const allRainbows = document.querySelector(".rainbow");
10observer.observe(allRainbows);

<$>[注] 查看 這篇我們的帖子 另一個例子,在瓦尼拉JavaScript中使用Intersection Observer API。

视觉组成部分

考虑到上面的示例,您可能对如何实现 LazyImage.vue 组件有一个想法:

 1[label LazyImage.vue]
 2<template>
 3  <img :src="https://cdn.jsdelivr.net/gh/andsky/tutorials-images/srcImage" />
 4</template>
 5
 6<script>
 7export default {
 8  props: ['src'],
 9  data: () => ({ observer: null, intersected: false }),
10  computed: {
11    srcImage() {
12      return this.intersected ? this.src : '';
13    }
14  },
15  mounted() {
16    this.observer = new IntersectionObserver(entries => {
17      const image = entries[0];
18      if (image.isIntersecting) {
19        this.intersected = true;
20      }
21    });
22
23    this.observer.observe(this.$el);
24  },
25}
26</script>

我们正在设置观察者在安装链接中,确保该组件已经连接到DOM. 我们可以使用this.$el访问组件元素,然后将其传输到观察方法。

然后,我们在计算属性中使用的状态中有一个交叉的旗帜,即srcImage,所以当它与视图端口交叉时,它会返回 src prop 的实际值,但否则它会返回一个空串,浏览器不会加载任何东西。

关于表演的一篇文章

请记住,观察元素需要内存和CPU,这就是为什么我们不需要就停止观察它们的重要原因。

我们在IntersectionObserver实例中有几种方法:

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

由于在我们的情况下,我们只有一个元素,任何一个元素都会正常工作:

 1[label LazyImage.vue]
 2<template>
 3  <img :src="https://cdn.jsdelivr.net/gh/andsky/tutorials-images/srcImage" />
 4</template>
 5
 6<script>
 7export default {
 8  props: ['src'],
 9  data: () => ({ observer: null, intersected: false }),
10  computed: {
11    srcImage() {
12      return this.intersected ? this.src : '';
13    }
14  },
15  mounted() {
16    this.observer = new IntersectionObserver(entries => {
17      const image = entries[0];
18      if (image.isIntersecting) {
19        this.intersected = true;
20        this.observer.disconnect();
21      }
22    });
23
24    this.observer.observe(this.$el);
25  },
26  destroyed() {
27    this.observer.disconnect();
28  }
29}
30</script>

首先,我们在设置this.intersected = true后立即停止观察,因为图像在那个时候已经被加载,继续观察就没有意义。

此外,如果它被破坏,观察某个组件没有意义,这就是为什么我们添加了破坏链接,以便在发生这种情况时停止观察它。

如果您尝试使用图片列表的LazyImage组件,并在浏览器的DevTools中打开网络卡,您将看到这些图像正在加载,当它们进入视图端口时。

<$>[注] 如果您对 Vue 应用程序中的懒惰加载图像感兴趣,但不希望自己实现交叉观察员代码,则可以查看 vue-clazy-load组件。

包装上

Lazy Loading 图像可以提高页面性能,特别是 页面加载时间和时间到交互式性能指标。

IntersectionObserver API 尚未得到所有现代浏览器的完全支持(https://caniuse.com/#feat=intersectionobserver),但有 polyfill由 w3c 维护,因此您今天可以在应用中使用它。

保持冷静

Published At
Categories with 技术
Tagged with
comments powered by Disqus