ほとんどのメディア形式と同様に、画像は非常に重く、読み込みにかなりの時間がかかる場合があります。 私たちはWeb開発者として、可能な限り圧縮し、Retinaディスプレイ用に2倍のバージョンを用意し、必要に応じて遅延読み込みするように教えられてきました。

問題は、画像を遅延ロードすることがいつ意味があるのかということです。 ええと、画像がページの上部にある場合、それは最初から見える可能性があります。 その場合、遅延読み込みは必要ありません。 ただし、画像がスクロール後にのみ表示される場合、または画像のリスト/グリッドの一部である場合(Google画像検索を考えてください)、画像を遅延ロードするのは理にかなっています。

交差点オブザーバーAPI

以前は、ページ上の要素の可視性を検出することは困難でした。 開発者はそれを自分で実装するか、そのためのライブラリを使用する必要があり、多くの場合、結果が遅く、エラーが発生しやすいソリューションになります。

Intersection Observer API は、この問題を非常に巧妙でパフォーマンスの高い方法で解決します。 これは、要素がビューポートに入ったときに通知されるように観察できるサブスクライブ可能なモデルを提供します。

簡単な例を次に示します。

const observer = new IntersectionObserver(entries => {
  const rainbowDiv = entries[0];
  if (rainbowDiv.isIntersecting) {
    // Do something cool here
  }
});

const rainbowDiv = document.querySelector("#rainbowDiv");
observer.observe(rainbowDiv);

IntersectionObserverのインスタンスを作成し、そのパラメーターとしてサブスクライブコールバックを渡す必要があります。 そこで、メソッドisIntersectingを使用して、rainbowDivと交差するかどうかを確認します。 最後に、observeメソッドを呼び出す必要があります。このメソッドでは、要素または要素のリストを渡します。

お気づきかもしれませんが、サブスクライブコールバックはエントリの配列を受け取ります。つまり、複数の要素を監視できます。

const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // ...
    }
  });
});

const allRainbows = document.querySelector(".rainbow");
observer.observe(allRainbows);

バニラJavaScriptでIntersectionObserverAPIを使用する別の例については、この投稿を確認してください。

LazyImageVueコンポーネント

上記の例を考えると、おそらくLazyImage.vueコンポーネントを実装する方法を知っているでしょう。

LazyImage.vue
<template>
  <img :src="srcImage" />
</template>

<script>
export default {
  props: ['src'],
  data: () => ({ observer: null, intersected: false }),
  computed: {
    srcImage() {
      return this.intersected ? this.src : '';
    }
  },
  mounted() {
    this.observer = new IntersectionObserver(entries => {
      const image = entries[0];
      if (image.isIntersecting) {
        this.intersected = true;
      }
    });

    this.observer.observe(this.$el);
  },
}
</script>

mountedフックにオブザーバーを設定し、コンポーネントがすでにDOMに接続されていることを確認します。 this.$elを使用してコンポーネント要素にアクセスし、それをobserveメソッドに渡すことができます。

次に、計算プロパティsrcImageで使用する状態のintersectedフラグがあるため、ビューポートと交差すると、の実際の値が返されます。 src propですが、そうでない場合は空の文字列を返し、ブラウザは何もロードしません。

パフォーマンスに関する注記

要素の監視はメモリとCPUを消費することに注意してください。そのため、必要がなくなったらすぐに要素の監視を停止することが重要です。

IntersectionObserverインスタンスにはいくつかのメソッドがあります。

  • unobserve:要素の監視を停止します。
  • disconnect:すべての要素の監視を停止します。

この場合、要素は1つしかないため、どちらも正常に機能します。

LazyImage.vue
<template>
  <img :src="srcImage" />
</template>

<script>
export default {
  props: ['src'],
  data: () => ({ observer: null, intersected: false }),
  computed: {
    srcImage() {
      return this.intersected ? this.src : '';
    }
  },
  mounted() {
    this.observer = new IntersectionObserver(entries => {
      const image = entries[0];
      if (image.isIntersecting) {
        this.intersected = true;
        this.observer.disconnect();
      }
    });

    this.observer.observe(this.$el);
  },
  destroyed() {
    this.observer.disconnect();
  }
}
</script>

まず、this.intersected = true;を設定した直後は、その時点で画像が既に読み込まれていて、観察を続けても意味がないため、観察を停止します。

さらに、コンポーネントが破壊された場合にコンポーネントを監視することは意味がありません。そのため、destroyedフックを追加して、その発生時にコンポーネントの監視を停止しました。

LazyImage コンポーネントを画像のリストで試して、ブラウザのDevToolsで[ネットワーク]タブを開くと、画像がビューポートに入るときに画像が読み込まれていることがわかります。

Vueアプリでの画像の遅延読み込みに興味があるが、交差点オブザーバーコードを自分で実装することに興味がない場合は、vue-clazy-loadコンポーネントを確認できます。

まとめ

画像の遅延読み込みにより、ページのパフォーマンス、特にページの読み込み時間とインタラクティブのパフォーマンス指標を向上させることができます。 IntersectionObserver APIを使用すると、独自のLazyImageコンポーネントを簡単に作成し、アプリで使用できます。

IntersectionObserver APIは、現在のすべてのブラウザで完全にはサポートされていませんが、 w3cによって維持されるpolyfillがあるため、アプリで使用できます今日。

涼しくしてください🦄