JavaScriptの5つの優先度とその使い方
すべてのスクリプトが同じではありません。正しい順序で読み込んでください。
すべてのスクリプトが同じではありません
1つのことは常に明確です。すべてのJavaScriptが同じというわけではありません。一部のスクリプトは、「メニューの操作」や「カートに追加」といった重要なインタラクションを処理します。一方で、はるかに重要性の低いスクリプトもあります。サイトを離脱しようとする訪問者にアンケートを促す、「exit intent」ポップアップスクリプトを例に挙げてみましょう。後者がなくても困りませんが、前者がなければウェブサイトの操作は非常に困難になります。
しかし、「一般的なウェブサイト」では、技術レベルでこの区別がされることはほとんどありません。すべてのJavaScriptは単にページに追加され、後はブラウザ任せになっています。ブラウザには何が重要で何が重要でないか分からないため、これは問題です。開発者である私たちには分かります。この問題を解決しましょう。
最終レビュー: Arjen Karel (2026年3月)
2025 Web Almanacによると、モバイルページのJavaScript読み込み量の中央値は、22リクエストで646KBです。そのJavaScriptの約44%は一度も実行されません。Lighthouseのrender blockingリソース監査に合格するページはわずか13%です。問題はJavaScriptを読み込む量だけではありません。いつ、どの順番で読み込むかです。
JavaScriptの優先順位がCore Web Vitalsに与える影響
適切な考慮なしにスクリプトをページに追加するだけで、3つのCore Web Vitalsすべてに影響を与える可能性があります。Largest Contentful Paint、Interaction to Next Paint、そしてCumulative Layout Shiftです。

例: render blockingなJavaScriptによってLCPのネットワークリソースが遅延している
Largest Contentful Paintは、帯域幅とCPUの競合の影響を受けやすいです。多すぎるスクリプトが初期のネットワークリソースを巡って競合すると、Largest Contentful Paintのネットワークリソースが遅延します。また、初期のCPU処理がmain threadをブロックし、LCPを遅延させます。
Interaction to Next Paintは、インタラクションの直前に実行されるスクリプトの影響を受ける可能性があります。スクリプトが実行されるとmain threadがブロックされ、その実行時間中のインタラクションが遅延します。
スクリプトが「ページの見え方を変更」する場合、Cumulative Layout Shiftを引き起こす可能性もあります。ページにバナーを挿入する広告スクリプトやスライダーは、この原因としてよく知られています。
5種類のJavaScript優先順位
私はJavaScriptの優先順位を5種類に分類しています。
- レンダークリティカル: これらは最も避けるべきスクリプトです。ページのレイアウトを変更するため、読み込まれなければレイアウトが全く異なるものになります。例: 一部のスライダースクリプトやA/Bテスト。
- クリティカルなスクリプト: ページの重要な機能を処理します。これらのスクリプトがなければ、カートへの商品追加、サイト内検索、ナビゲーションといったクリティカルなタスクは実行できません。
- 重要なスクリプト: 重要なビジネスロジックを処理し、サイトはこれらに依存しています。例: アナリティクス。
- Nice to Haveなスクリプト: あると便利ですが、いざという時にページを機能させるために必要というわけではありません。例: チャットウィジェットやexit intent。
- 将来のスクリプト: クリティカル、またはNice to Haveなスクリプトかもしれませんが、実際に使用する前に「他のステップ」を踏む必要があるため、今は必要ありません。例: 複数ステップのチェックアウトスクリプト。
各優先順位レベルを見る前に、Chromeが異なるスクリプトタイプにネットワーク優先順位を割り当てる仕組みを紹介します。
| スクリプトの種類 | Chromeの優先順位 | レンダリングをブロックするか? |
|---|---|---|
<head>内の<script> | Highest | はい |
<script async> | Low(ダウンロード) / High(実行) | いいえ |
<script defer> | Low | いいえ |
<body>の最後にある<script> | Medium / High | いいえ |
これらのデフォルトを理解することが、リソースの優先順位付けの鍵となります。
1. レンダークリティカルなスクリプト
これらのスクリプトは、ページのレイアウトを直接変更します。これらがないと、ページは意図したデザインと全く異なるものになります。例として、読み込みプロセスの早い段階でレイアウトを変更するスライダーやA/Bテストフレームワークのスクリプトが挙げられます。
これらのスクリプトの問題は、deferや遅延ができないことです。少しでも遅れるとウェブサイトのレイアウトがシフトし、UXの低下やCore Web Vitalsの悪化を招きます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Page Title</title>
<link href="styles.css" rel="stylesheet" />
<script src="render-critical.js"></script>
</head>
<body></body>
</html>
ベストプラクティス:
- 可能な限り、レンダークリティカルなスクリプトは避けてください。コードを書き直し、この種のスクリプトへの依存をなくします。
- 回避できない場合は、インライン化するか、絶対に必要な部分だけを読み込んでください。
- これらのスクリプトにdeferやasyncを使用しないでください。headの先頭に配置し、「可能な限り早い」ダウンロードをトリガーします。
2. クリティカルなスクリプト
これらのスクリプトは、基本的なインタラクションを可能にします。これらがないと、サイトのナビゲーション、カートへの商品追加、Cookie通知、検索の実行といったクリティカルなタスクが不可能になります。サイトのコア機能に不可欠です。
これらのスクリプトは、asyncまたはdefer属性を使用してページのheadに配置する必要があります。これら2つのアプローチの詳細な比較については、asyncとdeferの比較、およびCore Web Vitalsへの影響を参照してください。
<script defer src="critical.js"></script>
<script async src="critical.js"></script>
ベストプラクティス:
- このようなスクリプトは最小限に抑え、この機能を他の重要度の低い機能と組み合わせないでください。
- 依存関係に応じて、
asyncまたはdeferを使用してこれらのスクリプトを早期に読み込みます。 - Real User Monitoringを使用して実行のボトルネックを特定し、パフォーマンスがユーザーのニーズを満たしていることを確認してください。
3. 重要なスクリプト
これらのスクリプトはビジネスをサポートしますが、ページが機能するために必須ではありません。例えばアナリティクスのスクリプトは不可欠なデータを提供しますが、より重要な視覚要素の前に読み込む必要はありません。クリティカルなスクリプトと重要なスクリプトの区別は議論になりやすいため、この優先順位を設定する前に必ずすべての関係者と話し合ってください。
この種のスクリプトの優先順位を下げるには、2つの確実な方法があります。
<html>
<head>
<!-- method 1: inject after DOMContentLoaded -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var script = document.createElement('script');
script.src = 'important.js';
document.body.appendChild(script);
});
</script>
</head>
<body>
<!-- method 2: place at the bottom of the page -->
<script defer src="important.js"></script>
</body>
</html>
1: DOMContentLoadedの後に挿入する
DOMContentLoadedイベントの後にスクリプトを挿入することで、HTMLが完全に解析された直後にスクリプトのダウンロードを開始させることができます。これにより、画像やフォントなどの検出可能(discoverable)なリソースを優先できます。この方法はバランスに優れています。機能の遅延を避けるのに十分早い段階でスクリプトの読み込みを開始しつつ、初期のページレンダリングに不可欠な初期リソースとは競合しません。
2: ページの最後に配置する
これは、ブラウザがドキュメント全体を処理するまでスクリプトの読み込みをdeferする古典的なテクニックであり、テクニック1とほぼ同じ結果をもたらします。唯一の違いは、テクニック1がブラウザのpreload scannerをスキップするのに対し、このテクニックはスキップしない点です。preload scannerは、クリティカルなリソースを素早く特定してキューに入れるためにブラウザが使用する、軽量で高速なスキャナーです。viewport内にlazy loadingの画像がある可能性がある場合はpreload scannerをスキップするのがよいですが、preload scannerを使用すればこのスクリプトの読み込みは高速化されます。
fetchpriorityに関する注意: deferされたスクリプトにfetchpriority="low"を指定すれば優先順位が下がると期待するかもしれません。しかし、下がりません。deferされたスクリプトやasyncスクリプトは、ChromeではすでにLow優先順位で読み込まれます。それらにfetchpriority="low"を追加しても何も起こりません。fetchpriority属性が影響を与えるのはブロッキングスクリプトのみであり、優先順位をHighestからHighに下げることができます。JavaScriptをdeferするその他の方法については、完全なガイドをご覧ください。
4. Nice-to-Haveなスクリプト
これらのスクリプトはUXを向上させますが、サイトが機能するために必須ではありません。例として、チャットウィジェット、カスタマーフィードバックのポップアップ、追加のアニメーションなどが挙げられます。
これらのスクリプトは、「lazy on load」と呼ばれるパターンの理想的な候補です。これは、ページのloadイベントを待ち、アイドル時間中にスクリプトを挿入することを意味します。loadイベントを待つことで、スクリプトがより重要な初期リソースと帯域幅やCPUで競合しないようにできます。アイドル時間を待つことで、ブラウザがユーザー入力などのより重要なタスクを処理していないことを保証します。
以下は動作する例です。
window.addEventListener("load", () => {
const idle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
idle(() => {
const script = document.createElement("script");
script.src = "/path/to/script.js";
document.head.appendChild(script);
});
});
SafariはrequestIdleCallbackをサポートしていないため、setTimeoutのfallbackが必要です。実行をより小さなチャンクに分割する必要があるスクリプトでは、scheduler.yield()を使用してmain threadの応答性を維持し、INPスコアを保護することを検討してください。
ベストプラクティス:
- ページの読み込み後、これらのスクリプトをlazy loadingし、アイドル時間を待ちます。
- このパターンで読み込まれたスクリプトは、高速な読み込みが保証されないことを理解してください。
CoreDashがモニタリングしているサイト全体で、非クリティカルなスクリプトをloadイベント後にdeferしているページのINPは、84%が「good(良好)」をスコアしています。これに対し、すべてのスクリプトをeagerに読み込んでいるページでは61%です。
5. 将来のスクリプト
将来のスクリプトとは、特定の条件が満たされるまで必要にならないスクリプトです。例えば、複数ステップのチェックアウトスクリプトは、ユーザーがカートに商品を追加した後にのみ関連性が出ます。これらのスクリプトは、ユーザーのジャーニーのずっと後まで待つことができます。
この例を見てください。IntersectionObserverを使用し、フォームが可視viewportに入ったときなど、JSロジックが必要な場合にのみ読み込みます。
<!DOCTYPE html>
<html>
<head>
<script>
document.addEventListener("DOMContentLoaded", function () {
const form = document.querySelector("form");
const observer = new IntersectionObserver(function (entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const script = document.createElement("script");
script.src = "/sign-up.js";
document.head.appendChild(script);
observer.unobserve(form);
}
});
});
observer.observe(form);
});
</script>
</head>
<body>
<form action="/sign-up" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
<button type="submit">Sign Up</button>
</form>
</body>
</html>
ベストプラクティス:
- ユーザーのアクションをトリガーとして、これらのスクリプトをオンデマンドで読み込みます。
- コード分割(code-splitting)技術を使用し、各ステップで必要な部分のみを配信します。
- ユーザーが特定のセクションにスクロールしたときなど、必要な場合にのみ動的に挿入します。
ほとんどのサイトに必要なパターンは2つだけです。機能的なものにはdefer、それ以外のすべてにはload+idleパターンです。スクリプトのfetchpriorityの微調整に時間を費やしているなら、おそらく複雑に考えすぎています。まずは大きな成果を手に入れましょう。できるものはdeferし、できるものは遅延させ、不要なものは削除してください。