Web

たくさんの Web Components を利用するページを改善する

投稿日:2021年10月15日 更新日:

1. はじめに

昨日の記事では、Web Components を使って複数のコンポーネントを 1つのWebページ上に作成する方法を紹介しました。

Web Components

1つのWebページに複数のHTML要素サンプルを記述する

2021.10.14

今回は、そこで紹介したコードを改善します。

2. 改善点その1(CLS)

まず、CLS (Cumulative Layout Shift) の問題に対処します。

昨日の内容の場合、Webページがレンダリングされる前の時点で、Web Component の表示サイズを計算するための情報がないために、必要なサイズが確保されないまま最初のレンダリングが行なわれてしまいます。そしてその後、Web Component が画面上に適用される段階になってはじめて表示サイズが決定するため、「今まで何もなかった場所に、Web Component が表示され、その下の要素の位置がずれる」現象が発生してしまいます(これが CLS です)。

この問題に対応するには、カスタム要素の幅と高さを CSS で指定すればよいのですが、カスタム要素に対して width と height を指定しても効かないので、カスタム要素をラッパー要素で囲んでおき、ラッパー要素の方に大きさを指定しておきます。

HTML

<div class="wrapper">
  <my-element></my-element>
</div>

CSS

div.wrapper {
  width: 400px;
  height: 225px;
}

aspect-ratio プロパティを使ってもよいです。

div.wrapper {
  width: 400px;
  aspect-ratio: 16 / 9;
}

3. 改善点その2(Web Component の遅延読み込み)

次に、ファーストビュー(最初に表示される画面の範囲)より下にある Web Component を、必要になったとき(画面がスクロールされたとき)になってはじめて読み込ませることで、Webページアクセス時の転送データ量を少なくすることができます(<img>要素の loading=lazy 属性と同じことをさせるイメージです)。

但し、ファーストビュー上に表示される(可能性のある)Web Component を遅延読み込みすると、LCP (Largest Contentful Paint) というパフォーマンス指標の値が悪化する可能性があります。簡単に言うと、アクセス時の画面表示が遅くなるかもしれない、ということです。ですので、厳密にはそちらは遅延読み込みさせない方がよいです。とはいえ、たいして影響がないのならまとめて遅延読み込みでもよいと思います。

以下が、遅延読み込みのサンプルコードになります。遅延せずに JavaScript ファイルをインポートするコードは削除します(ファーストビュー上の Web Component を表す JavaScript ファイルの読み込みは残しておいてもよいです)。このコードは、<body>タグ内の最後あたりに追記します。

<script type="module">
// querySelectorAll()の引数をうまく調整して、処理の対象とするカスタム要素を取得します
let targetElements = [].slice.call(document.querySelectorAll('main > div > *'));

// コールバック関数を登録して、IntersectionObserver のオブジェクトを生成します
let myObserver = new IntersectionObserver((entries, observer) => {
  // 対象となる画像要素毎に処理を行います
  entries.forEach((entry) => {
    // この要素が画面に入ってきた場合
    if (entry.isIntersecting) {
      // この要素を画面に追加する
      let myElm = entry.target;
      let tagName = myElm.localName; // カスタムタグの名前
      // JavaScriptファイルをインポートします
      // カスタムタグの名前が、対応するJavaScriptファイル名になっている前提です
      import(`./${tagName}.js`)
        .then((module) => {
          //console.log(module.default);
          // インポートしたモジュールが、module にセットされています
          // JavaScript ファイルで定義したクラスをカスタムタグ名に紐付けます
          customElements.define(tagName, module.default);
        })
        .catch(err => {
          console.log(err);
          });

      // 表示したカスタムタグは観察対象から外します
      myObserver.unobserve(myElm);
    }
  });
});
targetElements.forEach((elm) => {
  // 各要素の観察を開始します
  myObserver.observe(elm);
});
</script>
  • Intersection Observer API を使って、観察対象となる要素が画面に入ってきそうになったら、JavaScriptファイルをインポートしています。
  • なるべくコメントに、何をやっているのか書きました。

3. おわりに

2つの改善点を紹介しました。

2つ目は大変そうに思えるかもしれませんが、これくらいのコード量で遅延読み込みが実現できるのであれば、少し頑張ってみる価値はあると思います。

4. 参考

5. 関連

Web

Intersection Observer API を使った画像の遅延読み込み

2019.03.29

📂-Web

執筆者:labo


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

Web

rel=”preload” によってリソースを先読みさせる

目次1. rel=”preload” について読み込みは非同期に実行され、レンダリングをブロックしない通常の読み込み処理の記述が別に必要2. デモページ3. その他の特徴as …

Web

sizes属性のデフォルト値は 100vw である

sizes属性のデフォルト値は 100vw です。

Chrome

Webページが読み込まれる際、HTMLは分割されてパースされます

Webページが読み込まれる際、HTMLが分割されて段階的にパースされる様子を観察します。

Chrome

Chrome の「ピクチャー イン ピクチャー」機能を使って、YouTube の動画を最前面で再生する

目次1. Chrome の「ピクチャー イン ピクチャー」機能2. ピクチャー イン ピクチャーを行う方法3. プレイヤーの操作など3. おわりに 1. Chrome の「ピクチャー イン ピクチャー …

Glitch

Glitch に DokiWiki を設置しようとして失敗しました

Glitch に DokuWiki を設置しようとしたのですが、残念ながら失敗してしまいました。ここにその記録を残しておこうと思います。