宣言型部分更新

公開日: 2026 年 5 月 19 日

ウェブは、当初の静的なドキュメント主導のメディアから大きく進化しました。最新の機能豊富なウェブアプリは、コミュニケーション、購入、リッチ コンテンツの消費、複雑な生活の管理など、さまざまな理由で誰もが使用しています。

HTML は、さまざまな進歩を遂げたにもかかわらず、コンテンツの準備ができたタイミングやユーザーがコンテンツを消費するタイミングをほとんど考慮せずに、上から下へと順番に配信されます。CSS を使用するとコンテンツの順序を変更できますが、アクセシビリティに大きな副作用が生じることがよくあります。JavaScript では、さまざまな API を介して DOM を操作することで、この制約からある程度解放されますが、多くの場合、冗長な構文や、HTML に接続するための DOM ツリーの構築が必要になります。

ウェブでは、メディアのクライアント サーバーの性質上、パフォーマンスが非常に重要ですが、HTML の順序付けの性質を回避するために最適ではない選択がなされることが多く、パフォーマンスが低下します。これには、ページ全体が準備できるまで待つことや、重いフレームワークを使用してコンポーネントを非同期で配信することが含まれます。JavaScript フレームワークの人気は、ウェブ デベロッパーがウェブの起源の厳格なドキュメントのメンタルモデルよりもコンポーネント ベースのモデルを好んでいることを示しています。

Chrome チームはこの問題を検討し、宣言型部分更新という名前でウェブ プラットフォームに新しい追加機能を開発してきました。

2 つの新しい API セットにより、HTML ドキュメント自体が順不同である場合でも、新しい JavaScript API を使用して既存のドキュメントに HTML を動的に挿入する簡単な方法でも、HTML をより線形でない方法で簡単に配信できるようになります。これらは、Chrome 148 以降で chrome://flags/#enable-experimental-web-platform-features フラグを使用してデベロッパー テストを行う準備が整っています。ポリフィルも利用できるため、まだサポートしていないブラウザでも、これらの新しい API をすぐに使用できます。

ウェブ プラットフォームへのこれらの追加は、他のブラウザ ベンダーや標準化機関からの肯定的なフィードバックを受けて標準化されています。これらの新しい API を含めるよう、関連する標準が更新されています。

順不同ストリーミング

最初の変更セットは、<template> HTML 要素と処理命令プレースホルダを使用する新しい順不同ストリーミング API です。次に例を示します。

<div>
  <?marker name="placeholder">
</div>

...

<template for="placeholder">
  Here is some <em>HTML content</em>!
</template>

処理命令は XML には以前から存在していましたが、HTML ではコメントとして扱われ、無視されていました。この新しい API では、処理命令が HTML に導入されます。ブラウザが <?marker name="placeholder"> 処理命令を認識しても、以前と同様にすぐには何も行いませんが、後で参照できます。

<template> 要素は、name 属性を使用して対応する処理命令を検索し、コンテンツを置き換えます。この場合、解析後の DOM は次のようになります。

<div>
  Here is some <em>HTML content</em>!
</div>

置換用の <?marker> 属性の他に、テンプレートが処理される前に一時的なプレースホルダ コンテンツを表示できる <?start><?end> の範囲マーカーもあります。

<div>
  <?start name="another-placeholder">
  Loading…
  <?end>
</div>

...

<template for="another-placeholder">
  Here is some <em>HTML content</em>!
</template>

この場合、<template> が表示されるまで Loading… が表示され、その後新しいコンテンツに置き換えられます。

テンプレートに処理手順を含めて、複数の更新を許可することもできます。

<ul id="results">
  <?start name="results">
  Loading…
  <?end>
</ul>

...

<template for="results">
  <li>Result One</li>
  <?marker name="results">
</template>
...

<template for="results">
  <li>Result Two</li>
  <?marker name="results">
</template>
...

解析後の HTML は次のようになります。

<ul id="results">
  <li>Result One</li>
  <li>Result Two</li>
  <?marker name="results">
</ul>

ドキュメントに後で <template for="results"> が追加された場合に備えて、最後に最終処理の指示を記述します。

デモ

この動画では、ストリーミング HTML を使用して基本的なフォトアルバム アプリケーションを実装しています。

順不同ストリーミングで実装されたフォトアルバムのデモ(ソース

ステータスと写真は、初期レイアウト後に HTML にストリーミングされます。

ユースケース

ストリーミング HTML と組み合わせると、この順序外の HTML パッチ適用には多くのユースケースがあります。

  • アイランド アーキテクチャ。Astro などのフレームワークで普及した一般的なパターンであるアイランド アーキテクチャでは、コンポーネントが静的 HTML の上で個別にハイドレーションされます。<template for> API を使用すると、静的コンテンツを HTML で直接同様に処理できます。JavaScript フレームワークは、よりインタラクティブなアイランドやコンポーネントの処理にもこれを使用できます。
  • 準備ができたらコンテンツを配信します。このアイランド アーキテクチャにより、コンテンツは準備が整ったときにストリーミングできます。データベース検索など、追加の処理が必要なコンテンツのために保留されることはありません。多くのプラットフォームでは HTML のストリーミングが許可されていますが、HTML の順序付けの性質上、コンテンツが遅延したり、複雑な JavaScript DOM 操作に頼らざるを得ないことがよくあります。これで、待機中に静的コンテンツを配信し、HTML ストリームの最後に高価なコンテンツを挿入できます。
  • HTML をページの読み込みパフォーマンスに最適な順序で配信できます。さらに、準備が整った後でも注文を変更できます。たとえば、メガメニューは一般的なナビゲーション機能ですが、インタラクティブになるまでユーザーに表示されない HTML が多く含まれています。この大きな HTML のチャンクは、HTML ドキュメントの後半で配信して、初期ページの読み込みに必要なより重要な HTML を優先できます。HTML では順序は障壁になりません。

これらはユースケースの一部にすぎません。デベロッパーがこの新しい API をどのように活用するのか、楽しみです。

制限事項と注意点

この API には、注意すべき制限事項と微妙な違いがいくつかあります。

  • セキュリティ上の理由から、<template for> は同じ親要素内の処理命令のみを更新できます。<template for><body> 要素に直接追加すると、ドキュメント全体(<head> を含む)にアクセスできるようになります。
  • <?end> 処理命令は省略可能です。省略した場合、<?start> 要素と包含要素の末尾の間のコンテンツが置き換えられます。
  • <template for> のストリーミング開始後に処理手順を移動すると、新しいコンテンツが古い場所にストリーミングされ続けるという予期しない結果が生じる可能性があります。
  • setHTMLinnerHTML などのメソッドで <template for> を動的に挿入する場合、テンプレートの解析時の「親」は中間ドキュメント フラグメントになります。つまり、これらのメソッドを使用して HTML を挿入しても既存の DOM は変更されず、パッチ適用はフラグメント内で「インプレース」で行われます。ただし、streamHTMLUnsafe などのメソッド(後述)を使用してストリーミングする場合、中間フラグメントがないため、テンプレートで既存のコンテンツを置き換えることができます。

今後追加される可能性のある機能

今後追加される可能性がある機能として、次のようなものが検討されています。

  • クライアントサイドのインクルード。例: <template for="footer" patchsrc="/partials/footer.html">
  • バッチ処理。クライアントサイドでは、フラグメント インクルードを拡張してバッチ処理を処理し、複数の更新が同時に行われるようにすることもできます。
  • 変更されないコンテンツの上書きを防ぐ。これは、コンテンツのリビジョン番号またはバージョニングで実現できます。これにより、コンテンツをリセットするのではなく、ルートの変更やその他の更新の間で状態を維持できます。
  • パッチ適用中のサニタイズ。例: <template for=icon safe><svg id="from-untrusted-source">...</svg></template>

ポリフィル

Chrome チームは、他のブラウザにこの機能が実装される前からサイトでこの新機能をすぐに利用できるように、template-for-polyfill をリリースしました。npm で入手できます

ブラウザの HTML パーサーを直接更新できないなど、いくつかの制限がありますが、最も一般的なユースケースはカバーされています。サイトは他のブラウザでもテストする必要があります。

HTML の挿入とストリーミングの方法を更新

すべてのコンテンツを HTML で配信できるわけではありません。この分野で Chrome が行っている作業の 2 つ目の部分は、JavaScript を通じてコンテンツを簡単に更新できるようにすることです。

JavaScript を使用して既存のドキュメントに HTML を動的に挿入する方法はすでに複数あります。

  • setHTML
  • setHTMLUnsafe
  • innerHTMLouterHTML のセッター
  • createContextualFragment
  • insertAdjacentHTML

ただし、これらはすべて動作がわずかに異なり、デベロッパーが常に考慮するとは限らない微妙な違いがあります。

  • 新しいコンテンツは上書きされますか、追加されますか?
  • たとえば、<script> タグをエスケープして、潜在的に危険な HTML をサニタイズしますか?
  • そうでない場合、<script> は実行されるべきか?
  • Trusted Types との連携

これらの API を見て、それぞれの質問に自信を持って答えられるデベロッパーはほとんどいません。

大きな制限事項として、事前にわかっている HTML の完全なセットに対してのみ使用できることが挙げられます。これは、HTML のストリーミングを許可する呼び出しがあった場合です。実際には、HTML の強みの一つであるコンテンツの即時ストリーミング機能が使えなくなり、コンテンツを挿入する前にコンテンツ全体をダウンロードする必要があることを意味します。ペイロードを分割したり、document.write などのハック的な非推奨メソッドを使用したりすることで、限定的に回避できますが、それぞれに問題があります。

静的 API とストリーミング API の新しいセット

Chrome は、この問題を解決し、ストリーミング機能を導入するために、既存の setHTMLsetHTMLUnsafe新しい API と拡張機能のスイートを提案しています。

設定または置換するメソッドと、既存の HTML の前後にコンテンツを挿入するメソッドがあります。各メソッドにはストリームの同等のメソッドがあります。

アクション 静的 ストリーミング
要素の HTML コンテンツを設定する setHTML(html, options); streamHTML(options);
要素全体をこの HTML に置き換えます replaceWithHTML(html, options); streamReplaceWithHTML(options);
要素の前に HTML を追加する beforeHTML(html, options); streamBeforeHTML(options);
HTML を要素の最初の子として追加する prependHTML(html, options); streamPrependHTML(options);
HTML を要素の最後の子として追加する appendHTML(html, options); streamAppendHTML(options);
要素の後に HTML を追加する afterHTML(html, options); streamAfterHTML(options);
新しい挿入方法とストリーミング方法

Unsafe バージョンもあります。これについては後ほど説明します。多くのメソッドがあるように見えるかもしれませんが(特に Unsafe の同等のメソッドを追加した場合)、一貫した命名規則により、前述の関連性のないメソッドよりも各メソッドの機能が明確になります。

静的バージョンでは、新しい HTML を DOM 文字列引数として受け取り、オプションのオプションも受け取ります。

const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTML(newHTML);

ストリーミング バージョンは、getWriter() などの Streams API で動作します。

const contentElement = document.querySelector('#content-to-update');
const writer = contentElement.streamHTMLUnsafe().getWriter();

// Example stream of updating content
while (true) {
 await writer.write(`<p>${++i}</p>`);
 await new Promise((resolve) => setTimeout(resolve, 1000));
}

writer.close();

または、フェッチ レスポンスから、パイプチェーンを使用します。

const contentElement = document.querySelector('#content-to-update');

const response = await fetch('/api/content.html');

response.body
  .pipeThrough(new TextDecoderStream())
  .pipeTo(contentElement.streamHTMLUnsafe());

また、中間 TextDecoderStream() ステップを必要とせずに直接ストリーミングできる便利なメソッドを追加する予定です。

options 引数を使用すると、カスタム sanitizer を指定できます。デフォルトは defaultデフォルトのサニタイザー構成)です。次のように使用します。

const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');

// Only allows basic formatting
const basicFormattingSanitzer = new Sanitizer({ elements: ["em", "i", "b", "strong"] });

contentElement.setHTML(newHTML, {sanitizer: basicFormattingSanitzer});

「安全でない」メソッド

各 API には「安全でない」バージョンもあります。

アクション 静的 ストリーミング
要素の HTML コンテンツを設定する setHTMLUnsafe(html,options); streamHTMLUnsafe(options);
要素全体をこの HTML に置き換えます replaceWithHTMLUnsafe(html, options); streamReplaceWithHTMLUnsafe(options);
要素の前に HTML を追加する beforeHTMLUnsafe(html, options); streamBeforeHTMLUnsafe(options);
HTML を要素の最初の子として追加する prependHTMLUnsafe(html, options); streamPrependHTMLUnsafe(options);
HTML を要素の最後の子として追加する appendHTMLUnsafe(html, options); streamAppendHTMLUnsafe(options);
要素の後に HTML を追加する afterHTMLUnsafe(html, options); streamAfterHTMLUnsafe(options);
「安全でない」挿入メソッドとストリーミング メソッド

これらの「安全でない」メソッドは、デフォルトでサニタイザーをオフにします(必要に応じてカスタム サニタイザーを指定できます)。また、スクリプトをオプションの runScripts オプション(デフォルトは false)で実行することもできます。

setHTML と同様に、setHTMLUnsafe は既存のメソッドですが、スクリプト実行で使用できるように runScripts オプション パラメータが追加されています。

const newHTML = `<p>This is a new paragraph</p>
                 <script src=script.js></script>`;
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTMLUnsafe(newHTML, {runScripts: true});

メソッドの「安全でない」という文言は、潜在的なリスクと、スクリプトをサニタイズまたは制限する方法をデベロッパーに思い出させるためのものであり、これらのメソッドを使用すべきではないという意味ではありません。

この「安全でない」の程度は、入力の信頼性によって異なります。Unsafe 静的メソッドはすべて、html 引数として DOM 文字列または TrustedHTML の両方で動作し、サニタイザーの使用も許可します。ただし、runScript の目的はスクリプトを許可することであるため、デフォルトではサニタイザーは使用されません。

ユースケース

これらの新しい API により、デベロッパーは既存のページに HTML を簡単に追加できるようになり、一貫した名前とオプションで新しい API を追加できます。ストリーミング API を使用すると、新しいコンテンツがすべてプラットフォームで利用可能になるまで待つ必要がないというパフォーマンス上のメリットが得られます。

次のようなユースケースがあります。

  • シングルページ アプリでの大規模なコンテンツ更新の動的ストリーミング。前述のとおり、現在の SPA の大きな欠点は、初期 HTML ロードのストリーミングの性質を活用できないことでした。しかし、それも過去の話です。
  • HTML フッターなどの共通コンテンツを挿入する。JavaScript API を使用すると、パーシャルをプルしてページに挿入できます。これにより、送信されるすべてのページでパーシャルを繰り返すのではなく、キャッシュ保存のメリットを享受できます。ただし、実行に JavaScript が必要になるため、これは初期読み込みで表示されないコンテンツにのみ使用してください。

これらはほんの一例です。皆様のアイデアをぜひお聞かせください。

制限事項と注意点

これらの新しい API には、注意すべき制限と微妙な違いもいくつかあります。

  • Trusted Types API とのストリーミングの統合には、新しい createParserOptions メソッドを使用する必要があります。このメソッドを使用すると、サニタイザーを任意の HTML 設定オペレーションに挿入できます。信頼できる型の統合の詳細については、説明をご覧ください
  • <template for> と同様に、ストリーミング中の要素を移動すると、予期しない結果やストリーム エラーが発生する可能性があります。
  • streamHTMLUnsafe は、多くの点でメイン パーサーに似た動作をします。たとえば、<template for> 命令がメイン ドキュメントに追加されるとすぐに処理し、defer スクリプトはストリームの最後まで遅延させます。

ポリフィル

Chrome チームは、他のブラウザにこの機能が実装される前からサイトでこの新機能をすぐに利用できるように、html-setters-polyfill をリリースしました。npm で入手できます

このポリフィルはストリーミングを行わず、完了時にバッファリングして適用します。これは、機能というよりは API の形状のポリフィルです。

また、安全なコンテンツの設定は setHTMLSanitizer API に依存しますが、Safari ではサポートされていません。

両方を併用する

これらは 2 つの別々の API ですが、真の力はこれらを組み合わせることで生まれます。新しい <template for> 要素を HTML にストリーミングすることで、DOM への個別の JavaScript 参照で各要素を直接ターゲットにすることなく、コンテンツのさまざまな部分を動的に更新できます。

基本的な SPA スタイルのページ読み込みは、処理手順を含むアウトライン ページを読み込み、各新しいページのテンプレートを HTML の下部にストリーミングして、それらの処理手順にスロットインすることで実装できます。

これらの API には、間違いなくさらなる可能性とユースケースが存在します。Google の(限られた)想像力に制限されることなく、部分的な更新を管理しやすくすることで、ボイラープレート コードを減らし、更新を容易にし、ウェブの新たな可能性を切り開くことができます。