Svelte

Load-функции: +page.js и +page.server.js

Страница карточки товара в интернет-магазине должна показать товар, отзывы и остаток на складе - и всё это до того, как пользователь увидит экран, иначе будет мигание спиннеров. В чистом Svelte данные тянулись бы из onMount уже после монтирования компонента, то есть после первого пустого рендера. SvelteKit переворачивает порядок: сначала функция load собирает данные, и только потом компонент рендерится с готовым результатом. Один и тот же load работает и на сервере при первой загрузке, и на клиенте при навигации.

  • Карточка товара: title, цена, отзывы и остаток приходят в load до рендера, поисковики видят полный HTML
  • Дашборд аналитики: server load ходит в БД напрямую, секреты подключения не утекают в браузер
  • Лента статей: universal load дергает публичный CMS API одинаково на сервере и при клиентской навигации
  • Профиль пользователя: load по params.username запрашивает нужного пользователя, без жёстко зашитого id
  • Vercel и Netlify деплои на SvelteKit: server load исполняется в serverless-функции, отдаёт уже наполненную страницу

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

  • Файловый роутинг SvelteKit: папки routes, +page.svelte, динамические сегменты [param]
  • Промисы и async/await в JavaScript, понятие fetch
  • Идея SSR: первый ответ страницы формируется на сервере

Что такое load и как данные попадают на страницу

В SvelteKit рядом с +page.svelte можно положить файл load-функции. Он экспортирует функцию с именем load, которая возвращает обычный объект. Этот объект приходит в страницу как проп data. До того как компонент отрисуется, фреймворк дожидается завершения load, поэтому разметка сразу получает наполненные данные, а не пустоту с последующей дозагрузкой.

Проп data объявляется через `$props`, как любой входной параметр компонента в Svelte 5. Поле data.product это ровно то, что вернул load. Разработчик не пишет ни onMount, ни ручного состояния загрузки - к моменту рендера данные уже на месте.

Аргумент load это объект event. Из него берут params (значения динамических сегментов маршрута), url (адрес со строкой запроса), fetch (специальная обёртка над браузерным fetch), а также route, setHeaders и parent. Деструктуризация в сигнатуре делает видимым, какие именно поля использует данная функция.

Как данные, возвращённые из load, попадают в +page.svelte?

Universal load против server load

Есть два файла для load с разным поведением. Universal load живёт в +page.js и выполняется в двух средах: на сервере при первом заходе на страницу и в браузере при последующей клиентской навигации. Server load живёт в +page.server.js и выполняется только на сервере, всегда. Выбор между ними определяется тем, что именно делает функция.

Свойство+page.js (universal)+page.server.js (server)
Где исполняетсяСервер при первом заходе, браузер при навигацииТолько на сервере
Доступ к БД и секретамНет - код уходит и в браузерДа - код в браузер не попадает
Что можно вернутьЛюбые значения, включая классы и функцииТолько сериализуемое (через devalue)
Типичная задачаПубличный API, логика без секретовПрямой запрос в БД, чтение приватных env, сессия

Импорт из `$lib/server` (как database выше) физически невозможно затащить в клиентский бандл - SvelteKit падает на сборке, если такой модуль просочится в universal-код. Это защита от случайной утечки серверных секретов в браузер.

Результат server load сериализуется при передаче в браузер с помощью библиотеки devalue, которая умеет больше, чем JSON: Date, Map, Set, BigInt. Но функцию или экземпляр своего класса через границу сервер-клиент так не отправить. Universal load таких ограничений не имеет, потому что при навигации он уже исполняется в самом браузере.

Страница ходит в базу данных напрямую и читает приватный ключ из переменных окружения. В каком файле должен жить её load?

Params, fetch и параллельная загрузка

Динамические сегменты маршрута приходят в load через params. Для маршрута src/routes/products/[id] значение params.id равно тому, что стоит в адресе. Так одна страница обслуживает любой товар без жёстко зашитых идентификаторов. Строку запроса (например ?sort=price) читают через url.searchParams.

Передаваемый в load fetch это не голый браузерный fetch, а обёртка SvelteKit. Она понимает относительные пути (/api/products резолвится относительно текущего хоста и на сервере тоже), наследует cookie и заголовки исходного запроса при работе на сервере, а для внутренних эндпоинтов может ответить без сетевого похода. Поэтому внутри load всегда берут fetch из аргумента, а не глобальный.

Если страница тянет несколько независимых ресурсов, последовательные await создают водопад: второй запрос стартует только после первого. Независимые запросы запускают одновременно через Promise.all - суммарное время становится равно самому медленному из них, а не сумме.

Если внутри load обращений несколько и они не зависят друг от друга, привычка оборачивать их в Promise.all почти всегда уместна. Сначала запускаются все промисы, и только потом начинается ожидание - так ни один запрос не простаивает в очереди за предыдущим.

Внутри load два независимых запроса написаны как два отдельных await подряд. В чём проблема и как её решить?

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

Load-функции - вход данных на страницу. Дальше курс раскрывает запись и продвинутые паттерны:

  • API-эндпоинты +server.js — Серверный код для произвольных HTTP-запросов, когда данные нужны не одной странице, а многим клиентам
  • Паттерны загрузки данных — Параллельная загрузка, depends и invalidate, стриминг промисов из load
  • Form actions — Обратная сторона load: отправка и изменение данных через серверные actions

Итог

  • Load-функция собирает данные до рендера и возвращает объект, который страница получает через проп data
  • +page.js содержит universal load: он исполняется и на сервере (при первом заходе), и в браузере (при навигации)
  • +page.server.js содержит server load: всегда только на сервере, поэтому в нём доступны БД, секреты и приватные ключи
  • Аргумент event даёт params, url, fetch и другие поля; обёрнутый SvelteKit fetch умеет относительные пути и передаёт cookie
  • Независимые запросы внутри одного load стоит запускать через Promise.all, чтобы они шли параллельно, а не водопадом

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

  • sv-21-server-routes — Server load и +server.js оба выполняются на сервере, но решают разные задачи: load кормит конкретную страницу, +server.js отдаёт переиспользуемый HTTP-эндпоинт
  • sv-23-data-loading-patterns — Когда базовый load освоен, следующий шаг - убирать водопады, грузить параллельно и стримить промисы
  • sv-20-form-actions — Load читает данные для отображения, form actions их изменяют. Вместе это полный цикл чтения и записи на странице
Load-функции: +page.js и +page.server.js

0

1

Войти