Chrome Canary представляет textStream() и набор методов потокового DOM,
которые меняют подход к передаче HTML из fetch() на страницу. Разберём, как
они работают и когда их применять.
Стриминг — один из самых эффективных паттернов производительности в вебе. Вместо ожидания полной загрузки ресурса браузер обрабатывает данные инкрементально — отображая контент по мере поступления. Новые методы потокового DOM в Chrome Canary встраивают эту возможность непосредственно в слой DOM API.
Раньше для стриминга HTML на страницу требовалась ручная оркестровка: разбор байтового потока
из response.body, пропускание через TextDecoderStream и ручное
добавление фрагментов в DOM. Новые API устраняют большую часть этого boilerplate-кода.
Метод textStream() доступен на объектах Response и
Blob. Он возвращает ReadableStream строковых фрагментов,
декодированных из байтового потока как UTF-8 текст.
Чтобы получить текстовый поток из fetch(), нужно было вручную пропустить его через декодер:
const response = await fetch('/partial.html');
const decoder = new TextDecoderStream();
const textStream = response.body.pipeThrough(decoder);
const reader = textStream.getReader();
// ... ручное чтение фрагментов и вставка в DOM
Вся цепочка сворачивается в один вызов:
const response = await fetch('/partial.html');
const textStream = response.textStream();
// textStream — ReadableStream строковых фрагментов
// Готов к передаче в методы потокового DOM
textStream() эквивалентен пропусканию байтового потока через UTF-8
TextDecoderStream — но без настройки. Он также работает с
Blob:
const blob = new Blob(['<h1>Привет</h1><p>Мир</p>']);
const stream = blob.textStream();
// Используйте тот же паттерн потока
Chrome Canary добавляет 12 новых методов на Element, которые принимают текстовый
поток и рендерят его в DOM инкрементально. Они разделены на две категории:
safe (всегда санитизируют) и unsafe (без санитизации
по умолчанию).
Эти методы всегда очищают HTML, удаляя скрипты и потенциально опасные элементы:
streamHTML() — заменяет содержимое элемента потоковым HTMLstreamReplaceWithHTML() — заменяет сам элементstreamAppendHTML() — добавляет потоковый HTML последним потомкомstreamPrependHTML() — вставляет потоковый HTML первым потомкомstreamBeforeHTML() — вставляет HTML перед элементомstreamAfterHTML() — вставляет HTML после элементаЭти методы не санитизируют по умолчанию, но принимают опции с sanitizer и/или runScripts:
streamHTMLUnsafe()streamReplaceWithHTMLUnsafe()streamAppendHTMLUnsafe()streamPrependHTMLUnsafe()streamBeforeHTMLUnsafe()streamAfterHTMLUnsafe(){runScripts: true}. Используйте для доверенного контента с вашего сервера.const feed = document.getElementById('chat-feed');
async function streamMessages() {
const response = await fetch('/api/chat/stream');
await response.textStream()
.pipeTo(feed.streamAppendHTML());
}
// Каждый фрагмент HTML с сервера отображается
// немедленно как новое сообщение в ленте
Вместе с потоковыми методами Chrome также вводит не-потоковые аналоги для замены устаревших DOM API. Полная картина:
| Устаревший метод | Новый (не-потоковый) | Stream Safe | Stream Unsafe |
|---|---|---|---|
innerHTML устар. |
setHTMLUnsafe новый |
streamHTML safe |
streamHTMLUnsafe unsafe |
outerHTML устар. |
replaceWithHTMLUnsafe новый |
streamReplaceWithHTML safe |
streamReplaceWithHTMLUnsafe unsafe |
insertAdjacentHTML(beforeend) устар. |
appendHTMLUnsafe новый |
streamAppendHTML safe |
streamAppendHTMLUnsafe unsafe |
insertAdjacentHTML(afterbegin) устар. |
prependHTMLUnsafe новый |
streamPrependHTML safe |
streamPrependHTMLUnsafe unsafe |
insertAdjacentHTML(beforebegin) устар. |
beforeHTMLUnsafe новый |
streamBeforeHTML safe |
streamBeforeHTMLUnsafe unsafe |
insertAdjacentHTML(afterend) устар. |
afterHTMLUnsafe новый |
streamAfterHTML safe |
streamAfterHTMLUnsafe unsafe |
Новые не-потоковые методы с Unsafe — прямая замена устаревшим. Они отличаются
от старых API в двух важных аспектах:
<template shadowrootmode>{runScripts: true} для выполнения скриптов
Безопасный не-потоковый метод setHTML и его потоковый аналог
streamHTML используют Sanitizer API
под капотом. Это тот же механизм, что и у класса Sanitizer: удаление скриптов,
обработчиков событий и другого опасного контента при сохранении безопасной разметки.
Один из самых интересных сценариев — декларативные частичные обновления.
Паттерн сочетает элементы <template> с серверными инструкциями обработки
для обновления конкретных секций страницы без JavaScript-селекторов.
Сервер передаёт HTML, содержащий <template for="имя">. Исходная страница
использует инструкции <?marker name="имя">:
<!-- Исходная разметка страницы -->
<main>
<?marker name="main-content">
</main>
<aside>
<?marker name="sidebar">
</aside>
Сервер отвечает определениями шаблонов:
<template for="main-content">
<article>
<h1>Руководство по стримингу HTML</h1>
<p>Контент загружается постепенно...</p>
</article>
</template>
<template for="sidebar">
<div class="widget">
<h3>Связанные темы</h3>
<ul>...</ul>
</div>
</template>
Потоковая передача соединяет их:
const response = await fetch('/page-with-partials.html');
await response.textStream()
.pipeTo(document.body.streamAppendHTMLUnsafe());
При использовании safe-методов помните, что <template> удаляются при
санитизации. Чтобы их сохранить, передайте пустой конфиг санитайзера:
await response.textStream()
.pipeTo(document.body.streamAppendHTML({sanitizer: {}}));
Атрибуты for и name определяют, какой template заменяет какой
маркер — без querySelector и ручного присваивания.
Если потоковый HTML содержит <script>, которые должны выполниться,
используйте unsafe-метод с опцией runScripts:
const response = await fetch('/dynamic-widget.html');
await response.textStream()
.pipeTo(
document.body.streamAppendHTMLUnsafe({runScripts: true})
);
Это осознанное проектное решение — safe-методы никогда не выполняют скрипты, а unsafe требуют явного согласия. Это предотвращает XSS-уязвимости даже при использовании unsafe-семейства.
На июнь 2026 года textStream() и методы потокового DOM доступны только в
Chrome Canary. Они ещё не вышли в Chrome Stable, и нет сигналов от Firefox
или Safari о планах по реализации.
Для продакшена используйте не-потоковые аналоги или полифиллы. Потоковые API идеальны для экспериментов, прогрессивного улучшения и подготовки к будущей поддержке браузеров.
Потоковая вставка новых сообщений в DOM по мере поступления с сервера — без WebSocket-обработчиков для рендеринга.
Загрузка компонентов дашборда прогрессивно — каждый виджет рендерится по мере поступления HTML-чанка.
Обновление секций страницы с сервера без клиентской маршрутизации и управления состоянием.
Передача серверного форматированного текста (markdown, ProseMirror) непосредственно в область контента.
Если вы сейчас используете innerHTML или insertAdjacentHTML для
вставки HTML из fetch(), вот путь миграции:
innerHTML на setHTMLUnsafe (или setHTML для санитизированного контента). Они работают сегодня и не требуют стриминга.textStream().pipeTo(element.streamAppendHTML()).
textStream() и методы потокового DOM — значительный шаг вперёд для
манипуляции DOM. Объединяя примитивы стриминга из Streams API с прямой вставкой в DOM,
Chrome делает создание страниц с прогрессивной загрузкой и реактивными обновлениями
значительно проще.
Пока это экспериментальные API, доступные только в Chrome Canary. Но паттерны, которые они вводят — декларативные частичные обновления, безопасная по умолчанию санитизация и стриминг как первоклассная концепция — вероятно, повлияют на будущее веб-разработки во всех браузерах.
Если вы создаёте веб-приложение с обновлениями в реальном времени, прогрессивной загрузкой или серверным частичным рендерингом, эксперименты с этими API сегодня подготовят вас к тому, куда движется платформа.
Создаёте приложение со стримингом, обновлениями в реальном времени или прогрессивной загрузкой? Могу помочь с архитектурой и реализацией. Бесплатная консультация.