宣告式部分更新

發布日期:2026 年 5 月 19 日,上次更新日期:2026 年 9 月 8 日

網路早已不再是靜態、以文件為主的媒體,現代人會使用豐富的網頁應用程式,原因有很多,包括通訊、購物、觀看多媒體內容,以及管理複雜的生活。

儘管 HTML 不斷進步,但仍會依序從上到下傳送,不太考慮內容何時準備就緒或使用者何時會使用。CSS 可讓您變更內容順序,但通常會對無障礙功能造成重大影響。JavaScript 可讓您透過各種 API 操作 DOM,擺脫這種限制,但這些 API 通常需要冗長的語法,或建構 DOM 樹狀結構來插入 HTML。

由於網路是採用用戶端/伺服器架構的媒體,因此效能非常重要。不過,為了規避 HTML 的依序性質,通常會做出次佳選擇,導致效能降低。包括等待整個頁面準備就緒,或使用大量架構以非同步方式傳送元件。JavaScript 架構的普及程度顯示,網頁程式開發人員偏好以元件為基礎的模型,而非網頁起源的嚴格文件心智模型。

Chrome 團隊一直在考慮這個問題,並以「宣告式部分更新」為名,開發網頁平台的新增功能。

前兩組新 API 可讓您更輕鬆地以非線性方式提供 HTML,無論是 HTML 文件本身順序錯亂,還是透過更簡單的方式,使用新的 JavaScript API 將 HTML 動態插入現有文件,都能輕鬆達成。您也可以使用 Polyfill,即使在尚未支援這些 API 的瀏覽器中,也能立即使用這些新 API。

無序串流

Browser Support

  • Chrome: 150.
  • Edge: 150.
  • Firefox: not supported.
  • Safari: not supported.

Source

第一組變更包括使用處理指令預留位置和 <template> HTML 元素 (含 for 屬性) 的新無序串流 API。例如:

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

...

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

處理指示在 XML 中存在已久,但在 HTML 中會視為註解並遭到忽略。這項新 API 變更可將處理指示帶入 HTML。舉例來說,當瀏覽器看到 <?marker name="placeholder"> 處理指令時,不會立即執行任何動作 (與先前一樣),但稍後可以參照該指令。

具有 for 屬性<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>

在這種情況下,系統會顯示 Loading… 字串,直到看到 <template> 為止,然後以新內容取代。

您也可以在範本中加入處理指令,允許進行多項更新:

<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 元素?

這是首次使用這個 API 的人常問的問題,建議使用 <slot> 甚至是 <template> 元素本身。這項提案的初始版本採用標準 HTML 元素,但隨著設計反覆修改,處理指令也隨之變更。處理指令可讓修補作業在不影響 DOM 的情況下進行。因此可用於 <head> (例如更新 <title>),甚至可用於 <table> 等其他元素內 (例如新增選用的額外資料列)。

雖然許多網頁開發人員不熟悉語法,但處理指令的彈性更大,且較不會發生回溯相容性問題。這些屬性已用於 XML,且現在是 HTML 標準的一部分,這都要歸功於這項提案。

示範

這部影片會使用串流 HTML 實作基本相簿應用程式:

使用無序串流實作的相簿示範 (來源)

初始版面配置完成後,狀態和相片都會串流至 HTML。

用途

搭配串流 HTML 使用時,這種 HTML 修補程式的用途相當廣泛:

  • 島嶼架構。Astro 等架構普及的常見模式是島嶼架構,其中元件會獨立於靜態 HTML 上進行水合作用。<template for> API 可讓您直接在 HTML 中,以類似方式處理靜態內容。JavaScript 架構也可以使用此功能,處理互動性更高的島嶼或元件。
  • 在內容準備就緒時交付。有了這種「島嶼」架構,內容準備就緒後即可串流,不必等待需要額外處理的內容 (例如資料庫查詢)。雖然許多平台都允許串流 HTML,但 HTML 的依序性質表示內容通常會遭到延遲,或必須採用複雜的 JavaScript DOM 操作。現在您可以在等待期間提供靜態內容,然後在 HTML 串流結尾插入更昂貴的動態內容。
  • 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> 屬性屬於 HTML 標準,但並非所有瀏覽器都支援。

未來可能新增的項目

我們正在考慮日後新增以下功能:

  • 用戶端包括。例如 <template for="footer" src="/partials/footer.html">,甚至不需修補 <template src="/partials/footer.html">。詳情請參閱說明影片。這項功能位於 chrome://flags/#enable-experimental-web-platform-features 旗標後方。
  • 避免覆寫不會變更的內容。這可以透過內容修訂編號或版本控管達成。這樣一來,系統就能在路徑變更或其他更新時維持狀態,而不是重設內容。
  • 修補時進行清理。例如:<template for=icon safe><svg id="from-untrusted-source">...</svg></template>

Polyfill

Chrome 團隊已發布 template-for-polyfill (可透過 npm 取得),讓網站在其他瀏覽器支援這項功能前,就能立即使用。

由於無法直接更新瀏覽器的 HTML 剖析器,因此有一些限制,但涵蓋最常見的用途。網站仍應在其他瀏覽器中進行測試。

更新 HTML 插入和串流方法

並非所有內容都能以 HTML 格式提供。Chrome 在這方面所做的第二項工作,是為了讓您更輕鬆地使用 JavaScript 更新內容。

目前已有許多方法可使用 JavaScript,將 HTML 動態插入現有文件中:

  • setHTML
  • setHTMLUnsafe
  • innerHTMLouterHTML
  • createContextualFragment
  • insertAdjacentHTML

不過,這些方法運作方式略有不同,開發人員可能不會總是考慮到這些細微差異:

  • 新內容是覆寫還是附加?
  • 是否會清除可能危險的 HTML,例如逸出 <script> 標記?
  • 如果不是,是否應執行 <script>'s?
  • 如何搭配信任的型別使用?

很少有開發人員能誠實地查看這些 API,並自信地回答每個 API 的問題。

但這項功能有很大的限制,就是只能用於預先知道的完整 HTML 組合,且必須先呼叫允許串流 HTML 的函式。實際上,這表示您必須先下載整個內容,才能插入內容,而 HTML 的優點之一就是能夠立即串流內容。您可以透過分割酬載或使用 document.write 等過時的權宜方法,在有限的範圍內解決這個問題,但這些方法會帶來其他問題。

一組全新的靜態和串流 API

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Chrome 已開發一系列新 API 和擴充功能,可取代現有的 setHTMLsetHTMLUnsafe,不僅能簡化程序,還能提供串流功能。

開發人員可從 Chrome 148 開始,使用 chrome://flags/#enable-experimental-web-platform-features 旗標測試這些功能,預計在 Chrome 155 中推出。

您可以設定或取代內容,也可以在現有 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);

串流版本可搭配 Streams API 使用,例如搭配 getWriter()

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());

textStream() 便利方法

Browser Support

  • Chrome: 151.
  • Edge: 151.
  • Firefox: not supported.
  • Safari: not supported.

此外,我們也新增了textStream 方便方法,讓您不必經過中介 TextDecoderStream() 步驟,即可直接串流:

const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');

response.textStream().pipeTo(contentElement.streamHTMLUnsafe());

options

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 靜態方法都可搭配 DOM 字串或 TrustedHTML 做為 html 引數,並允許使用清除器。不過,runScript 的整體意圖是允許指令碼,因此預設不會使用任何清除器。

用途

這些新 API 可讓開發人員更輕鬆地在現有網頁中新增 HTML,並新增名稱和選項一致的 API。使用串流 API 時,不必等到所有新內容都上傳到平台,就能開始播放,因此可提升效能。

用途包括:

  • 在單頁應用程式中動態串流播放大型內容更新。如先前所述,目前 SPA 架構的一大缺點是無法從初始 HTML 載入的串流特性獲益,但現在可以了!
  • 插入常見內容,例如 HTML 頁尾。使用 JavaScript API 即可擷取局部內容並插入網頁,享有快取功能,不必在每個傳送的網頁中重複這些內容。不過,由於這類內容必須透過 JavaScript 才能執行,因此只應適用於初始載入時不會顯示的內容。

再次提醒,以上只是幾個範例,我們很期待看到大家的作品!

限制和細微差異

這些新 API 也包含幾項限制和細微差異,請務必留意:

  • 如要將串流與 Trusted Types API 整合,必須使用新的 createParserOptions 方法,將清除器插入任何 HTML 設定作業。如要進一步瞭解可信類型整合,請參閱說明影片
  • <template for> 類似,移動串流中的元素可能會導致非預期的後果或串流錯誤。
  • streamHTMLUnsafe 在許多方面都更像主要剖析器,包括在 <template for> 指令新增至主要文件時處理這些指令,以及將 defer 指令延後到串流結尾。

標準化狀態

較新的插入和串流方法正在加入 HTML 標準,但並非所有瀏覽器都支援。

Polyfill

Chrome 團隊已發布 html-setters-polyfill (可透過 npm 取得),讓網站在其他瀏覽器支援這項功能前,就能立即使用。

請注意,這個 Polyfill 不會串流,而是會在完成時緩衝及套用。這比較像是 API 形狀的 Polyfill,而非功能。

此外,設定安全內容取決於 setHTMLSanitizer API,但 Safari 不支援這兩者。

兩者並用

雖然這兩個 API 各自獨立,但結合使用更能發揮效益。將新的 <template for> 元素串流至 HTML,即可動態更新不同部分的內容,不必使用個別的 JavaScript 參照 DOM 直接指定每個部分。

如要實作基本的 SPA 樣式網頁載入,可以載入含有處理指令的大綱頁面,然後將每個新網頁的範本串流至 HTML 底部,插入這些處理指令。

這兩項 API 肯定還有更多潛在用途,請盡情發揮想像力。簡化部分更新的管理作業,有助於減少樣板程式碼、簡化更新作業,並發掘網路的全新潛力!

Chrome 正在宣告式部分更新專案下開發更多 API,但我們很期待能先將這兩個 API 交到您手中。我們會隨時提供最新消息,並在有更多相關資訊時通知你。