asyncなscript要素で読み込むメリットと問題点
ページの読み込み速度は、秒の闘いで、3秒待たされたらお客が半分以下になる世界です。
ところが、JavaScriptやCSSなどは、外部からコード読み込んでいる間、ブラウザのレンダリングをブロックし最初のペイントを遅らせます。
script要素のasync属性
そこで、script要素にはasyncというコードの読み込みを遅延してくれる属性があります。
(仕様 w3c https://dev.w3.org/html5/spec-LC/scripting-1.html#the-script-element)
これをつけると、そのコードの読み込みを文字通り非同期に遅らせるので、その間ブラウザがロックするのを回避できます。
つまり、最初のペイントまでの時間が速くなるのです。
問題点
でも、問題もあります。非同期ですが、例えばjqueryなら標準の自動起動関数$(function(){})はうまく動作せず、DOMContentLoadedもライブラリの読み込み完了を教えてくれず、エラーが出ることがあります。この属性にはcallbackやawaitなどは無いのです。(このシリーズの3回目でscript要素自身のonloadによる対策ついて書いています)
今回は、例えば、メインのHTMLと読み込むライブラリとしてjqueryの2つのファイルを用意して操作を確認してみます。
コード
今回はアロー関数などは使わずにベーシックな書き方でいきます。
main.html
<script>
let i=0 //発火順序表示用の変数
</script>
<script async src="js/jquery-3.6.1.min.js?1"></script>
<script>
// DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log(++i,': DOMContentLoaded')
});
// window.onload
window.onload = function() {
console.log(++i,': window.onload ')
}
// jqueryの $(function(){})
$(function(){
console.log(++i,': $(function(){})')
})
</script>
jquery-3.6.1.min.js
jqueryファイルの最後にコードを追記します。
//省略
:
:
&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
// jquery の最後に読み込んだことを示すconsole.logを書きます
console.log(++i,':(in jqf) at the jquery file end')
結果
Uncaught ReferenceError: $ is not defined
1 ': (in jqf) at the jquery file end'
2 ': DOMContentLoaded'
3 ': window.onload '
これが何を意味するかというと、jquery が読込み終わる1の前に「$」が呼ばれて「$ is not defined」と言われてしまっています。
2,3のイベントはjquery が読込み後に発火しています。
いくつかのサイトでDOMContentLoadedやwindow.onloadは大丈夫と書かれているケースも見かけましたが、
実は、問題なのはこれです。Macで同じコードを試した結果です。
Uncaught ReferenceError: $ is not defined
1 ': DOMContentLoaded'
2 ': at the jquery file end'
3 ': window.onload '
「2 ': at the jquery file end'」の前に DOMContentLoaded が来てしまっています。つまり、DOMContentLoadedの中でもしjqueryのコードを使えば、jqueryはまだ読み込まれていないので、エラーになります。
やはり、非同期の読み込み終了がいつになるのかはこれではわからないという事のようです。
少なくともDOMContentLoaded も script loaded 後に発火するという保証のないことが証明されました。
対策
いつ読み込み終わるかわからなくては、いつエラーが出るかもわからず、使い物になりません。
それなら、どうしたらよいのでしょう?
そこで対策を考えてみました。つづく。
asyncなscript要素で読み込むライブラリと依存コードを同期的に実行する方法(2)
https://qiita.com/toshirot/items/c60d987378ff6bdbaf27
関連
次回の、、読み込むライブラリファイルに依存コードを同梱する方法
https://qiita.com/toshirot/items/c60d987378ff6bdbaf27
次々回の、script要素.onloadイベントで読み込み完了を取得する方法
https://qiita.com/toshirot/items/f454c9fdcc78f6d90d01