發布日期:2026 年 5 月 19 日
網路早已不再是靜態、以文件為主的媒體,現代人會使用功能豐富的網頁應用程式,原因有很多,包括通訊、購物、觀看多媒體內容,以及管理複雜的生活。
儘管 HTML 不斷進步,但仍會依序從上到下傳送,不太考慮內容何時準備就緒或使用者何時會使用。CSS 可讓您變更內容順序,但通常會對無障礙功能造成重大影響。JavaScript 可讓您透過各種 API 操控 DOM,擺脫這種限制,但通常需要詳細的語法或建構 DOM 樹狀結構,才能插入 HTML。
網路的用戶端/伺服器性質決定了效能的重要性,但為了規避 HTML 的依序性質,我們經常會做出次佳選擇,導致效能降低。包括等待整個頁面準備就緒,或使用大量架構以非同步方式傳送元件。JavaScript 架構的普及程度顯示,相較於網頁起源的嚴格文件心智模型,網頁開發人員更偏好以元件為基礎的模型。
Chrome 團隊一直在考慮這個問題,並以「宣告式部分更新」為名,開發網頁平台的新增功能。
兩組全新 API 可讓您更輕鬆地以非線性方式提供 HTML,無論是 HTML 文件本身順序錯亂,還是透過更簡單的方式,使用新的 JavaScript API 將 HTML 動態插入現有文件,都能輕鬆完成。從 Chrome 148 開始,開發人員可以使用 chrome://flags/#enable-experimental-web-platform-features 旗標測試這些功能。您也可以使用 Polyfill,即使在尚未支援這些 API 的瀏覽器中,也能立即使用這些新 API。
我們正在將這些網路平台新增內容標準化,其他瀏覽器供應商和標準化管道也給予正面評價。我們正在更新相關標準,將這些新 API 納入其中。
亂序串流
第一組變更內容是新的無序串流 API,使用 <template> HTML 元素和處理指令預留位置。例如:
<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>
在此情況下,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 實作基本相簿應用程式:
初始版面配置完成後,狀態和相片都會串流至 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>開始串流後才移動處理指示,新內容可能會繼續串流到舊位置,導致意想不到的後果。 - 請注意,使用
setHTML或innerHTML等方法動態插入<template for>時,範本在剖析時的「父項」是中間文件片段。也就是說,使用這些方法插入 HTML 無法修改現有 DOM,修補作業會在片段內「就地」進行。不過,使用streamHTMLUnsafe等方法串流時 (我們即將介紹!),沒有中間片段,因此範本可以取代現有內容。
未來可能新增的項目
我們正在考慮日後新增以下功能:
- 用戶端包括。例如:
<template for="footer" patchsrc="/partials/footer.html">。 - 批次處理。在用戶端,片段包含項目也可以擴充為處理批次作業,確保多項更新同時進行。
- 防止覆寫不會變更的內容。這可以透過內容修訂編號或版本控管達成。這樣一來,系統就能在路徑變更或其他更新之間維持狀態,而不是重設內容。
- 修補時進行清理。例如:
<template for=icon safe><svg id="from-untrusted-source">...</svg></template>
Polyfill
Chrome 團隊已發布 template-for-polyfill,並在 npm 上提供,讓網站即使在其他瀏覽器支援這項功能前,也能立即使用。
由於無法直接更新瀏覽器的 HTML 剖析器,因此有些限制,但最常見的用途都涵蓋在內。網站仍應在其他瀏覽器中進行測試。
更新 HTML 插入和串流方法
並非所有內容都能以 HTML 格式傳送。Chrome 在這方面進行的第二項工作,是讓使用者更容易透過 JavaScript 更新內容。
目前已有許多方法可使用 JavaScript,將 HTML 動態插入現有文件中:
setHTMLsetHTMLUnsafeinnerHTML和outerHTML設定器createContextualFragmentinsertAdjacentHTML
不過,這些方法運作方式略有不同,開發人員可能不會注意到其中的細微差異:
- 新內容是覆寫還是附加?
- 例如,是否會逸出
<script>標記,以清除可能有害的 HTML? - 如果不是,是否應執行
<script>? - 如何搭配 TrustedTypes 使用?
很少有開發人員能誠實地查看這些 API,並自信地回答每個 API 的問題。
但這項功能有很大的限制,就是只能用於預先知道的完整 HTML 組合,且必須先呼叫允許串流 HTML 的函式。實際上,這表示您必須先下載整個內容,才能插入內容,而 HTML 的優點之一就是能夠立即串流內容。您可以透過分割酬載或使用 document.write 等已淘汰的權宜方法,在有限的範圍內解決這個問題,但這些方法會帶來其他問題。
一組全新的靜態和串流 API
Chrome 提議一系列新 API 和現有 setHTML 和 setHTMLUnsafe 的擴充功能,可清理這項問題,並導入串流功能:
您可以設定或取代現有 HTML,也可以在現有 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());
我們也計畫新增便利方法,讓您直接串流,不必經過中繼 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 靜態方法都可搭配 DOM 字串或 TrustedHTML 做為 html 引數,並允許使用清除器。不過,runScript 的整體意圖是允許指令碼,因此預設不會使用清除器。
用途
這些新版 API 採用一致的名稱和選項,可讓開發人員更輕鬆地在現有頁面中新增 HTML。串流 API 的優點是不必等到所有新內容都上傳到平台,就能開始串流播放。
用途包括:
- 在單頁應用程式中動態串流播放大型內容更新。如先前所述,目前 SPA 的一大缺點是無法從初始 HTML 載入的串流性質獲益,但現在可以了!
- 插入常見內容,例如 HTML 頁尾。使用 JavaScript API 即可擷取局部內容並插入網頁,享有快取功能,不必在每個傳送的網頁中重複這些內容。不過,由於這類內容必須依附 JavaScript 才能執行,因此只應適用於初始載入時不會顯示的內容。
再次提醒,以上只是幾個範例,我們很期待看到大家的作品!
限制和細微差異
這些新 API 也包含幾項限制和細微差異,請務必留意:
- 如要將串流與 Trusted Types API 整合,必須使用新的
createParserOptions方法,將清理器插入任何 HTML 設定作業。詳情請參閱信任的型別整合說明 - 與
<template for>類似,移動串流中的元素可能會導致非預期的後果或串流錯誤。 streamHTMLUnsafe在許多方面都與主要剖析器類似,包括在<template for>指令新增至主要文件時處理這些指令,以及將defer指令延後至串流結尾。
Polyfill
Chrome 團隊已發布 html-setters-polyfill,並在 npm 上提供,讓網站即使在其他瀏覽器支援這項功能前,也能立即使用。
請注意,這個 Polyfill 不會串流,而是會在完成時緩衝及套用。這比較像是 API 形狀的 Polyfill,而非功能。
此外,安全內容的設定取決於 setHTML 和 Sanitizer API,但 Safari 不支援這兩者。
同時使用這兩項功能
雖然這兩個 API 各自獨立,但結合使用才能發揮真正的力量。將新的 <template for> 元素串流至 HTML,即可動態更新不同部分的內容,不必使用個別的 JavaScript 參照 DOM 直接指定每個部分。
您可以載入含有處理指令的架構頁面,然後將每個新網頁的範本串流至 HTML 底部,插入這些處理指令,即可實作基本的 SPA 樣式網頁載入。
這兩項 API 肯定還有更多潛在用途和使用案例,請盡情發揮想像力。簡化部分更新的管理作業,有助於減少樣板程式碼、簡化更新作業,並發掘網路的全新潛力!