2026-09-22
每日一文 · 长文精读

Out-of-Order HTML Streaming Moves from JS Frameworks into the Browser

无序HTML流式传输从JS框架进入浏览器

作者:Bruno Couriol · InfoQ 原文

摘要:无序HTML流式传输允许页面在完全加载前即可交互,现正被浏览器原生支持。该提案通过<template>元素的for属性和处理指令,实现声明式插入点。浏览器在数据到达时修补占位符,Chrome 150支持声明式功能,Safari和Firefox表示支持。此技术由Facebook BigPipe于2009年开创,现标准化于浏览器引擎中。

Out-of-order HTML streaming, a pattern that allows users to view and interact with a page before it fully loads, is making its way to web browsers.
无序HTML流式传输,一种允许用户在页面完全加载前查看和交互的模式,正在进入网络浏览器。
Under the proposal, the browser patches placeholders as data arrives via declarative HTML or matching JavaScript APIs.
根据该提案,浏览器在数据通过声明式HTML或匹配的JavaScript API到达时修补占位符。
Chrome 150 ships the declarative features, while Safari and Firefox have signaled support.
Chrome 150提供了声明式功能,而Safari和Firefox已表示支持。
The declarative proposal introduces out-of-order streaming with the standard <template> element via a new for attribute.
该声明式提案通过新的for属性,使用标准<template>元素引入了无序流式传输。
Developers declare single insertion points (e.g., <?marker name="profile">), or wrap temporary fallback content (e.g., <?start name="profile">Loading profile...<?end>).
开发者声明单个插入点(例如,<?marker name="profile">),或包裹临时回退内容(例如,<?start name="profile">正在加载个人资料...<?end>)。
When the server streams a matching <template for="profile"> later in the document, the HTML parser matches the identifier, removes the <?start> and <?end> markers along with any intermediate fallback nodes between them, and inserts the template’s contents into that DOM position.
当服务器稍后在文档中流式传输匹配的<template for="profile">时,HTML解析器匹配标识符,移除<?start>和<?end>标记及其之间的任何中间回退节点,并将模板内容插入到该DOM位置。
Developers may also include processing instructions in templates to allow multiple updates:
开发者还可以在模板中包含处理指令以允许多次更新:
<ul id="results"> <?start name="results"> Loading… <?end> </ul>
<ul id="results"> <?start name="results"> 正在加载… <?end> </ul>
<!-- Streamed later in the HTTP response --> <template for="results"> <li>Result One</li> <?marker name="results"> </template>
<!-- 稍后在HTTP响应中流式传输 --> <template for="results"> <li>结果一</li> <?marker name="results"> </template>
... <template for="results"> <li>Result Two</li> <?marker name="results"> </template>
... <template for="results"> <li>结果二</li> <?marker name="results"> </template>
After the template HTML is parsed and processed, the resulting HTML on the page becomes:
在模板HTML被解析和处理后,页面上的最终HTML变为:
<ul id="results"> <li>Result One</li> <li>Result Two</li> <?marker name="results"> </ul>
<ul id="results"> <li>结果一</li> <li>结果二</li> <?marker name="results"> </ul>
To prevent cross-component injection attacks (e.g., untrusted markup hijacking sensitive forms or navigation targets elsewhere in the document), a <template for> can strictly only patch processing instructions (markup nodes that begin with <? delimiters) located within its immediate parent element (i.e., its direct siblings and their descendants).
为了防止跨组件注入攻击(例如,不可信标记劫持文档中其他位置的敏感表单或导航目标),<template for>严格只能修补位于其直接父元素内(即其直接兄弟及其后代)的处理指令(以<?分隔符开头的标记节点)。
As an explicit specification exception, placing the template directly under <body> grants it global document scope, allowing deferred updates to reach elements inside the <head> tag or nested structural containers.
作为明确的规范例外,将模板直接放在<body>下可授予其全局文档范围,允许延迟更新到达<head>标签内或嵌套结构容器中的元素。
To extend streaming workflows to client-side scripting, the initiative pairs these markup primitives with a unified suite of static and streaming DOM insertion methods.
为了将流式工作流扩展到客户端脚本,该倡议将这些标记原语与一套统一的静态和流式DOM插入方法配对。
The specification defines a predictable matrix of operations: setHTML, replaceWithHTML, beforeHTML, prependHTML, appendHTML, and afterHTML, along with matching streaming methods (streamHTML, streamAppendHTML, etc.) and *Unsafe variants.
该规范定义了一个可预测的操作矩阵:setHTML、replaceWithHTML、beforeHTML、prependHTML、appendHTML和afterHTML,以及匹配的流式方法(streamHTML、streamAppendHTML等)和*Unsafe变体。
Invoking streamHTMLUnsafe(), for instance, returns a WritableStream that incrementally pipes incoming chunks into an internal HTML fragment parser.
例如,调用streamHTMLUnsafe()返回一个WritableStream,它增量地将传入的块通过管道传输到内部HTML片段解析器中。
The Fetch API complements this with the response.textStream() helper:
Fetch API通过response.textStream()辅助函数对此进行补充:
// Stream a dynamic HTML fragment directly into a target container const feedContainer = document.querySelector("#feed-container"); const response = await fetch("/api/feed-stream"); // Pipe UTF-8 text straight into the browser's fragment parser await response.textStream().pipeTo( feedContainer.streamHTMLUnsafe({ runScripts: false }) );
// 将动态HTML片段直接流式传输到目标容器中 const feedContainer = document.querySelector("#feed-container"); const response = await fetch("/api/feed-stream"); // 将UTF-8文本直接通过管道传输到浏览器的片段解析器中 await response.textStream().pipeTo( feedContainer.streamHTMLUnsafe({ runScripts: false }) );
By internalizing out-of-order delivery and rendering into the browser engine, the proposal standardizes rendering techniques that front-end frameworks have long implemented in different ways.
通过将无序交付和渲染内化到浏览器引擎中,该提案标准化了前端框架长期以来以不同方式实现的渲染技术。
Originally pioneered in 2009 by Facebook’s BigPipe and later popularized by frameworks such as React (with Suspense streaming) and Next.js, the differing techniques have the same underlying goal: to prevent a slow-to-compute part of the page from blocking the entire page.
最初由Facebook的BigPipe于2009年开创,后来由React(使用Suspense流式传输)和Next.js等框架推广,这些不同的技术有着相同的基本目标:防止页面中计算缓慢的部分阻塞整个页面。
The declarative markup primitives (<template for> and processing instruction targets) have been incorporated into the WHATWG HTML Living Standard, with initial support shipping in Chrome and Edge 150 alongside textStream() in version 151, while the companion JavaScript DOM streaming methods continue through a separate standardization process.
声明式标记原语(<template for>和处理指令目标)已被纳入WHATWG HTML Living Standard,初始支持在Chrome和Edge 150中提供,textStream()在151版本中提供,而配套的JavaScript DOM流式方法继续通过单独的标准化过程进行。
WebKit has published a positive standards position, and Mozilla has signaled receptive interest.
WebKit已发布了积极的标准化立场,Mozilla也表示出接受兴趣。

阅读理解

1. What is the primary purpose of the out-of-order HTML streaming proposal?

2. How does the declarative <template for> element handle security against cross-component injection attacks?

3. What is the role of the response.textStream() method in the Fetch API?

温故复习 →每日一句 →