Produced by FOURIER

JavaScriptの呼び出しにasyncを付けない方がよいときってどんなとき?

SenaSena calender 2022.10.14

はじめに

みなさま、JavaScriptコードの呼び出しはどうしていますか?

以下のようにasync属性やdefer属性を付けることによって、JavaScriptによるパーサーブロッキングを防ぐことが出来ます。

<script defer async src="/js/app.js"></script>

大体のJavaScriptコードを呼び出す際はこれで良いのですが、一部例外があります。

その一つがWeb ComponentsにおけるCustom Elementsの定義時です。

Custom Elementsの定義時に注意すること

Custom Elementsの定義時とは、つまりcustomElements.define()のことです。

CustomElementRegistry.define() - Web API | MDN

define() は CustomElementRegistry インターフェイスのメソッドで、新しいカスタム要素を定義します。

https://developer.mozilla.org/ja/docs/Web/API/CustomElementRegistry/define

例えばシンプルなWeb Componentsのコードを/js/app.jsに書いたとします。

'use strict';
const html = '<div style="display: flex; justify-content: center; align-items: center; color: white; background-color: #222; height: 1000px;">WebComponentsで動的に生成したHTML</div>';

class FrSample extends HTMLElement {
    constructor() {
        super();
        this._root = this.attachShadow({mode: 'closed'});
    }

    render() {
        this._root.innerHTML = `${html}`;
    }

    connectedCallback() {
        this.render();
    }
}

customElements.define('fr-sample', FrSample);

これを踏まえて以下のことに気を付けましょう。理由は後述します。

asyncまたはdeferでコードを呼び出さない

前述した通りです。

<script defer async src="/js/app.js"></script>

Custom Elementsを使うコードより前に定義する

Custom Elementsを使う前に、Custom Elementsを定義しましょう。

基本的には<head>内に書いておけば問題ありません。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta name="viewport" content="width=device-width, viewport-fit=cover"/>
    <meta charset="UTF-8"/>
    <title>SAMPLE</title>
    **<script src="/js/app.bundle.js"></script>**
</head>

<body>
        **<fr-sample></fr-sample>**
</body>

</html>

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta name="viewport" content="width=device-width, viewport-fit=cover"/>
    <meta charset="UTF-8"/>
    <title>SAMPLE</title>
</head>

<body>
        **<fr-sample></fr-sample>**
    **<script src="/js/app.bundle.js"></script>**
</body>

</html>

イベントを使って呼び出さない

何かの癖で、DOMContentLoadedリスナー内に書かないようにしましょう。

document.addEventListener('DOMContentLoaded', function () {
        customElements.define('fr-sample', FrSample);
});

jQueryについても同じです。

// import $ from 'jquery';

$(function () {
        customElements.define('fr-sample', FrSample);
});

$(document).ready(function () {
        customElements.define('fr-sample', FrSample);
});

パーサーブロッキングさせる理由

理由は簡単です。

Web Componentsが呼び出された際に、Layout Shiftと判定される可能性があるからです。 結果的にCore Web VitalsのCumulative Layout Shift(CLS)が悪くなってしまいます。

推奨する管理方法

パーサーブロッキングが必要なJavaScriptコードは、他のコードとは別ファイルにしましょう。

**<script defer async src="/js/common.bundle.js"></script> <!-- パーサーブロッキング不要 -->**
**<script src="/js/app.bundle.js"></script> <!-- パーサーブロッキング必要 -->**

コードが短い場合では、インライン化してしまうのもアリです。

**<script defer async src="/js/common.bundle.js"></script>**
**<script>'use strict';const html='<div style="display: flex; justify-content: center; align-items: center; color: white; background-color: #222; height: 1000px;">WebComponentsで動的に生成したHTML</div>';class FrSample extends HTMLElement{constructor(){super();this._root=this.attachShadow({mode:'closed'})} render(){this._root.innerHTML=`${html}`} connectedCallback(){this.render()}} customElements.define('fr-sample',FrSample)</script>**

まとめ

Core Web Vitalsの中でも、特にCLSはサーバーの処理性能が絡みにくいところです。そのため対策は楽な方だと思います。

常にLayout Shiftが起きないように、注意深くコーディングしていきましょう。

新しいメンバーを募集しています

Sena

Sena / Engineer

生涯に亘り技術を極めていきたい。