Magento2系でサイトを構築する際に、SEOやサイト表示のベストプラクティスに厳しい方からの意見として以下のようなものがあります。

できるだけJavaScriptの読込・実行を遅らせて、ページの描画を阻害しないようにしてください

Google Chromeの開発者ツールに含まれているAudit機能や、Lighthouse拡張を使っても同じようなアドバイスが上がってくるこの指摘。Magento2.3.1までは対応がなかなか面倒でした。
2.3.2で新たに機能追加が行われ、この対応ができるようになりました。

今回はこの機能紹介をしたいと思います。

そもそもこの指摘は何を言っているのか?

ブラウザがJavaScriptファイルを読み込むと、ブラウザはその内容を解釈して実行しようとします。
このとき、scriptタグにasyncやdefer属性をつけておくと、ページの読込や描画を阻害しないような振る舞いができます。
async属性もdefer属性も最近のブラウザはきちんと対応していますので、読み込むスクリプトの特性に合わせて宣言をすることで、ページの描画を妨げないようにすることができます。

ところが、これは外部ファイルにまとめたJavaScriptに限った話で、scriptタグにインライン要素として直接定義したJavaScriptの場合はこの限りではありません。
こういったスクリプトにはasyncもdeferも効かないため、これらのスクリプトは場合によってはページ読込や描画を邪魔してしまう恐れがあります。

この指摘ではそういったページの読込や描画を邪魔するスクリプトをHTML文書の先頭部分から排除し、HTML文書の可能な限り後ろに設置しましょうと言っています。
そうすることで外部ファイルの読込を可能な限り遅らせることができる他、インラインスクリプトの実行も送らせることができます。
スクリプトによっては要素が出現する前に実行されてしまうとエラーになるものもあるかもしれませんが、そういった問題も回避することができるでしょう。

Magento2.3.1までの問題点

Magento2.3.1までは、JavaScriptの読込はheadタグ内にscriptタグが出力されていました。
そのほか、一部のスクリプトが以下のようにインラインスクリプトとして定義されてきました。

    <script>
        require.config({
            deps: [
                'jquery',
                'mage/translate',
                'jquery/jquery-storageapi'
            ],
            callback: function ($) {
                'use strict';

                var dependencies = [],
                    versionObj;

                $.initNamespaceStorage('mage-translation-storage');
                $.initNamespaceStorage('mage-translation-file-version');
                versionObj = $.localStorage.get('mage-translation-file-version');

                
                if (versionObj.version !== 'yourlocalstorageversionnumber') {
                    dependencies.push(
                        'text!js-translation.json'
                    );

                }

                require.config({
                    deps: dependencies,
                    callback: function (string) {
                        if (typeof string === 'string') {
                            $.mage.translate.add(JSON.parse(string));
                            $.localStorage.set('mage-translation-storage', string);
                            $.localStorage.set(
                                'mage-translation-file-version',
                                {
                                    version: 'yourlocalstorageversionnumber'
                                }
                            );
                        } else {
                            $.mage.translate.add($.localStorage.get('mage-translation-storage'));
                        }
                    }
                });
            }
        });
    </script>

<script type="text/x-magento-init">
    {
        "*": {
            "mage/cookies": {
                "expires": null,
                "path": "/",
                "domain": "yourdomain",
                "secure": false,
                "lifetime": "3600"
            }
        }
    }
</script>

これらはMagentoが動作する上で必要なスクリプトではありますが、ページの描画を邪魔してしまう恐れもあり、できればページの先頭からは取り除きたい存在でした。
もともとMagento2系では、JavaScriptはRequireJSを用いてモジュールの依存関係を解決する仕組みになっています。
そのため、RequireJSのモジュールとして実装したJavaScriptであれば、自動的に外部スクリプトファイルの読み込みは実行の直前まで遅延させることができます。

とはいえ、前述のようなインラインスクリプトがHTML文書上に現れる場所を変更する機能は用意されておらず、カスタマイズ費用を掛けた上で対応するしかありませんでした。

2.3.2で実装された新機能

2.3.2では新たにscriptタグが出力される場所を変更するための機能が実装されました。
この機能は標準ではオフになっていますが、有効にすることでHTML文書上に出力されるscriptタグの位置をheadタグからbodyタグ終了付近に移動させることができます。

設定項目がある場所

この機能を制御する設定項目はMagentoの管理画面の、

店舗>設定>開発者向け

にあります。
この機能はproductionモードでは表示されません(メニュー自体が消えます)。表示させたい場合はdeveloperモードに切り替えてください。

設定画面にアクセスすると、次のような設定項目が表示されます。

設定画面

右端のチェックを外し、設定値を変更して保存すると、この機能が有効になります。

無効時のHTML

この機能が無効の場合は、次のようなHTMLが出力されます。
2.3.1までのMagentoでよく見かけたHTMLではないでしょうか。

無効時のHTML

有効時のHTML

機能を有効にした場合は、次のようなHTMLになります。

有効時のHTML

ぱっと見て分かる通り、HTMLの先頭にあったスクリプトが消えています。
これらはすべてHTMLの後ろに移動させられ、ページを読み込む処理の後半でようやく読み込まれる形になります。

注意点

この機能を有効にする場合、それまで正常に動作していたスクリプトが突如正しく動作しなくなる恐れがあります。
有効にする際は、テスト環境で十分にテストをした上で行ってください。

また、minifyやbundleを行う場合も同様で、十分に試験を行う必要があります。
標準のMagentoであればこの機能は正しく動作するように作られていますが、インラインスクリプトなどの書き方が適切でない場合はエラーになる恐れがあります。
十分ご注意ください。