定義
ポリフィル(旧ブラウザに無い関数を補うコード)を すべての 訪問者に無条件で渡すのではなく、「この機能は無いか?」をブラウザで実際に一行検査したうえで 不足したブラウザにだけ ロードする方式である。最新ブラウザは検知するだけでポリフィルを受け取らないので、コストがほぼ 0 になる。
ここでの feature detection(機能検知) は 'at' in Array.prototype のように機能の有無を直接テストするもので、User-Agent 文字列をパースしてブラウザを推測する方式より頑健だ(UA は偽装・欠落する)。
なぜ必要か
ポリフィルバンドルは簡単に数十 KB になる。これを全員に渡すと、ポリフィルがまったく不要な最新ブラウザ利用者(たいてい多数)まで毎回その分を追加で受信・パースする。対応すべき旧ブラウザが少数であるほど、この無駄が大きくなる。
差分ロードは、旧ブラウザには正確性を、最新ブラウザには性能を同時に与える。検知コストはインラインスクリプト数十バイトと非常に安く、実際のポリフィルダウンロードは旧ブラウザだけが支払う。
動作原理
- 対応ターゲットから欠損 API を計算して self-host ポリフィルバンドルを ビルド時に 作る。
<head>に小さな 同期 検知スクリプトを置く。代表的な API をいくつか検査する。- 一つでも無ければそのときだけポリフィルバンドルを同期的にロードする。全部あれば何も受け取らない。
- 検知スクリプトはアプリバンドルより 先に 実行されなければならない — そうすればアプリ起動時にポリフィルが準備できている。
<head>
<script>
// 代表的な API をいくつかだけ検査 — 一つでも無ければ旧ブラウザと判断
if (
!("at" in Array.prototype) ||
!("replaceAll" in String.prototype) ||
typeof structuredClone !== "function" ||
!("hasOwn" in Object)
) {
// 同期ロード: アプリバンドル実行前にポリフィルが準備されるように
document.write('<script src="/polyfills-legacy.js"><\/script>');
}
</script>
</head>
実務での適用
- 最新ブラウザでネットワークタブを開き「ポリフィル未受信」を確認する。
- 旧ブラウザ(または検知の強制)で「受信・動作」を確認する。
- ポリフィルは外部 CDN ではなく同一オリジンから提供する(サプライチェーン攻撃の実績・可用性リスクの回避)。
トレードオフ
| オプション | 最新ブラウザのコスト | 運用負担 | 適した状況 |
|---|---|---|---|
| エントリに静的に含める | +数十 KB 常時 | なし | 旧ブラウザが多数か、単純さを優先する場合 |
| ビルド時バンドル + feature-detect 条件ロード | ≈ 0(検知数十 B) | なし | ターゲットが「旧 vs 最新」の二分法のとき最適 |
| UA ベースのサーバー動的生成 | ≈ 0 | サーバー運用が必要 | ブラウザのスペクトルが非常に広いとき |
使うべきでない場合
- 対応ターゲットが実質最新のみでポリフィルが不要な場合 — 検知スクリプトすら蛇足。
- 検知スクリプトをアプリバンドルの後に置き、アプリがポリフィル無しで先に起動して初期に落ちる配置。
よくある失敗
- 検知を UA 文字列で行い、偽装・新規ブラウザに誤判定する。
- ポリフィルを外部 CDN から受け取り、サプライチェーン・可用性リスクを背負う。
- 検知スクリプトを async/defer に置いて実行順序がずれる。
関連概念
- syntax-transpilation-vs-runtime-polyfill — 何をポリフィルすべきか(構文ではなくランタイム API)
- readonly-viewer-bundle-splitting — 必要なときだけコードをロードするチャンク分割の考え方