Produced by Fourier

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

Sena Sena カレンダーアイコン 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 thumbnail

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)が悪くなってしまいます。

💡
上記の注意事項を守らなくても、ブラウザの処理順の関係によって`Layout Shift`と判定されない事もあります。

推奨する管理方法

パーサーブロッキングが必要な 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 slash forward icon Engineer

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

関連記事