Svelte

Стриминг данных из load

Страница товара в интернет-магазине: название, цена и фото берутся из быстрого кеша за 20 миллисекунд, а блок персональных рекомендаций считается медленной моделью три секунды. Если ждать рекомендации в load, посетитель три секунды смотрит на белый экран. Стриминг разрывает эту связку: SvelteKit отдаёт HTML с товаром мгновенно, а рекомендации досылает по тому же HTTP-ответу, когда они готовы. Браузер показывает спиннер только в одном блоке, а не на всей странице.

  • Карточки товаров: основные поля рендерятся сразу, а отзывы и похожие товары стримятся отдельными промисами
  • Дашборды: заголовок и навигация показываются мгновенно, а тяжёлые графики дотекают по мере готовности агрегаций
  • Ленты с персонализацией: общий шаблон ленты отдаётся сразу, а ранжированные под пользователя блоки стримятся
  • Страницы с внешними API: данные из медленного стороннего сервиса не блокируют отрисовку остального контента

Предварительные знания

  • Понимание load-функций: где они выполняются и что возвращают на страницу
  • Знание промисов в JavaScript: создание, await, обработка отклонения
  • Понимание того, что такое SSR и почему стриминг требует серверного рендеринга

Как промис из load превращается в поток

Обычный load ждёт все данные через await и возвращает готовый объект. Страница рендерится, когда самый медленный запрос завершён. Стриминг меняет правило: если в возвращаемом объекте оставить промис неразрешённым, SvelteKit не станет его ждать. Он отрендерит и отдаст HTML с уже доступными полями, а промис продолжит передаваться в том же HTTP-ответе и разрешится позже, когда данные придут.

Здесь product дождались через await, потому что без него страница бессмысленна. А getRecommendations вернули без await: это промис. SvelteKit отдаёт HTML с товаром немедленно и стримит рекомендации, когда модель досчитает. Различие в одном ключевом слове: await перед вызовом блокирует, его отсутствие делает поле стримящимся.

  • await в load (блокирующий) — Страница ждёт самый медленный запрос. Один экран, но первый байт приходит поздно. Подходит для критичных данных, без которых страница бессмысленна
  • Промис без await (стриминг) — HTML с быстрыми полями уходит сразу, медленные данные дотекают потоком. Первый экран мгновенный, но в блоке какое-то время виден каркас

Стримить промис можно только из серверного load (+page.server.js или +layout.server.js) и только при ssr=true. Универсальный load в +page.js при клиентской навигации возвращает данные обычным образом, а механизм потоковой передачи HTML относится именно к серверному рендерингу ответа.

В load из объекта возвращается поле reviews: fetchReviews() без await. Что сделает SvelteKit при серверном рендеринге?

Разворачивание стрима на странице через await

Стримящееся поле приходит на страницу как промис. Чтобы показать каркас, потом данные, а при сбое ошибку, используется блок {#await}. Он имеет три части: тело до then рендерится, пока промис в ожидании; ветка then получает разрешённое значение; ветка :catch получает причину отклонения. Это тот же блок, что и в обычном Svelte, но здесь он управляет именно потоковыми данными из load.

Поле data.product доступно сразу и рендерится без await: оно дождалось в load. А data.recommendations это промис, и блок await показывает скелетон, пока он не разрешится. Когда поток донесёт данные, Svelte заменит каркас списком. Если промис отклонится, сработает ветка :catch, и пользователь увидит сообщение об ошибке вместо повисшего спиннера.

Ветка :catch обязательна для стримящихся данных. Если её опустить, отклонение промиса не будет обработано на странице, и сбой медленного запроса даст необработанную ошибку в потоке вместо аккуратного фолбэка. Каждый стримящийся промис заслуживает свой :catch.

Пока стрим не завершён, HTTP-ответ остаётся открытым. SvelteKit вставляет недостающие данные через скрипт в конце потока, и Svelte разрешает промис на клиенте. Поэтому стриминг и требует SSR: именно открытый серверный ответ доносит поздние куски.

На странице стримящееся поле data.stats разворачивается блоком {#await data.stats}{:then s}...{/await} без ветки :catch. Запрос статистики на сервере падает с ошибкой. Что увидит пользователь?

Когда стриминг помогает, а когда вредит

Стриминг это компромисс, а не бесплатное ускорение. Он сокращает время до первого экрана, разрешая отдать HTML до готовности медленных данных. Но он же добавляет работу: каркасы, ветки ошибок, более сложный поток рендеринга. Выигрыш реален, когда на странице есть и быстрые, и заметно медленные данные. Если все запросы быстрые, стриминг только усложняет код без пользы.

СитуацияСтримить?Почему
Заголовок быстрый, рекомендации медленныеДаПервый экран мгновенный, медленный блок дотекает позже
Без этих данных страница бессмысленнаНетКаркас без сути это пустой экран, лучше await в load
Все запросы укладываются в 50 мсНетВыигрыша нет, только лишние каркасы и ветки ошибок
Данные нужны для SEO и индексацииНетСтримящийся блок может не попасть в HTML, который видит краулер

Критичные для SEO данные не стоит стримить. Поисковый робот часто читает первичный HTML, а стримящийся блок дотекает после него и может не попасть в проиндексированную версию. Заголовок, описание и основной контент, на который опирается выдача, нужно await-ить в load. Стриминг оставляют для вторичных блоков: рекомендаций, отзывов, аналитики, где задержка индексации не критична.

Практическое правило: один await в load для данных, без которых первый экран не имеет смысла, и промисы без await для всего, что медленно и второстепенно. Так первый байт приходит быстро, а пользователь не ждёт самый тормозной запрос ради остального контента.

Страница статьи блога: текст статьи нужен для SEO, а блок комментариев грузится из медленного стороннего сервиса. Как разумнее построить load?

Связь с другими темами

Стриминг надстраивается над load и серверным рендерингом, а на клиенте перекликается с асинхронным состоянием:

  • Load-функции — Стриминг это возврат промисов из того же load, который раньше возвращал готовые данные
  • Опции страницы — Стриминг включается только при ssr=true, что управляется опцией ssr
  • Асинхронные данные и реактивность — Тот же приём раннего каркаса и поздних данных применяется и на чистом клиенте

Итог

  • Стриминг достигается возвратом из load незавершённого промиса вместо await: SvelteKit отдаёт HTML сразу, а данные досылает потоком
  • Критичные данные нужно await-ить в load, а некритичные и медленные возвращать промисом, чтобы они не блокировали первый экран
  • На странице промис разворачивается блоком {#await ... then ... :catch}, который показывает каркас, затем данные или ошибку
  • Стриминг работает только при ssr=true, потому что опирается на постепенную передачу серверного HTML-ответа
  • Отклонённый стримящийся промис нужно обрабатывать в :catch, иначе ошибка проявится только в потоке, а не в load

Связанные уроки

  • sv-19-load-functions — Стриминг это особый способ возврата данных из load, поэтому базовое понимание load-функций обязательно
  • sv-28-page-options — Стриминг работает только при ssr=true, режим которого задаётся опциями страницы из предыдущего урока
  • sv-34-async-data — И стриминг, и асинхронные данные на клиенте решают одну задачу: показать каркас сразу, а данные позже
Стриминг данных из load

0

1

Войти