VueやReactなどのフレームワークで開発されたシングルページアプリケーションでは、特定のページで特殊な機能を開発する際には、しばしばサードパーティのJSファイルに依存する必要があります。グローバルにCDNリソースをインポートすると、冗長なファイルがロードされる可能性があるため、動的なロード方法を使用することが最善です。
動的なJSスクリプトの読み込みとは、依存ファイルを特定のページのみにインポートすることであり、グローバルにインポートすることを避けるため、これによりこれらのページが開かれていない場合に不要なリソースの読み込みを防ぎ、ページの読み込み速度を向上させ、プロジェクト全体をモジュール化することができます。
ドキュメントオブジェクトモデル(DOM)を使用すると、JavaScriptを使用してHTMLを動的に作成できます。<script>
要素も同様であり、他のページの要素とは何の違いもありません。したがって、JSファイルをロードするために<script>
を手動で作成することができます。
deferとasync#
<script>
要素には、defer
とasync
という2つの属性があり、それぞれ2つの異なるJSスクリプトのロードおよび実行モードを表します。
defer:このブール属性は、スクリプトがドキュメントの解析後に実行されることをブラウザに指示します。
async:このブール属性を設定すると、ブラウザにスクリプトを非同期に実行するように指示します。
defer
については、外部のjsをページの最後に配置すると考えることができます。jsの読み込みはページのレンダリングやリソースの読み込みをブロックしません。defer
は元のjsの順序で実行されます。
async
については、スクリプトを非同期にロードおよび実行できるようにする役割を果たします。これもページのレンダリングやリソースの読み込みをブロックしません。ダウンロードが完了するとすぐに実行されます。したがって、元の順序で実行されるとは限りません。複数のスクリプトファイルが相互に依存している場合、async
を使用するとエラーが発生する可能性が非常に高いです。
したがって、簡単に言えば、ブラウザはまずHTMLドキュメントを要求し、その後、さまざまなリソースの読み込みに対して対応するリソースローダーを非同期に呼び出し、DOMのレンダリングを行います。その後、<script>
タグに到達すると、メインプロセスはレンダリングを停止し、このリソースが完全にロードされてから実行し、その後DOM解析を続行します。 async
属性が追加されると、ロードと実行のために独自のプロセスが開始され、defer
は<script>
を<body>
の最後に配置するのと同じ効果があります。
上の図では、青い線はネットワーク読み取りを表し、赤い線は実行時間を表し、緑色の線はHTML解析を表しています。
const loadJS = (url, defer) => {
const recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', url)
if (defer) {
recaptchaScript.defer = true
} else {
recaptchaScript.async = true
}
recaptchaScript.onload = () => {
console.log('読み込み完了', url)
}
document.head.appendChild(recaptchaScript)
}
以下は例です。ここでは5つのJSスクリプトをロードしていますが、jquery-ui
とfullcalendar
はjquery
に依存しています。また、locale
はfullcalendar
に依存しています。このような場合、JSファイルを特定の依存関係の順序でロードして実行する必要があります。
const assets = [
'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.0/fullcalendar.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.0/locale/zh-cn.js',
]
assets.forEach((url) => loadJS(url, true))
現実は厳しい#
しかし、現実の環境では、ブラウザは遅延スクリプトを必ずしも順序通りに実行するわけではなく、DOMContentLoadedイベントが発生する前に実行されるわけでもありません。したがって、スクリプトファイルの実行順序を制御するためにdefer
のみを使用すると、非常に大きなリスクがあります。ただし、onload
イベントを監視してファイルがロードされたかどうかを判断し、Promise
を使用して前のスクリプトファイルのロードが完了するのを待ってから次のファイルをロードすることで、順序通りにスクリプトをロードして実行することができます。
const loadJS = (url) => {
return new Promise((resolve) => {
const recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', url)
recaptchaScript.defer = true
recaptchaScript.onload = () => {
resolve()
}
document.head.appendChild(recaptchaScript)
}).catch(console.error)
}
// JSファイルを順番にロード
const loadAssets = async () => {
for (const url of assets) {
await loadJS(url, true)
}
}
Enjoy it ฅ●ω●ฅ