Svelte / SvelteKitMiddleTechnical

В чём разница между функциями load в +page.ts и +page.server.ts?

+page.ts выполняется и на сервере (SSR) и в браузере (навигация), не имеет доступа к locals/cookies/БД. +page.server.ts работает только на сервере, имеет доступ к locals, cookies, секретам и может экспортировать Form Actions.

load в +page.ts vs +page.server.ts

Оба файла экспортируют функцию load, которая возвращает данные для +page.svelte через проп data. Ключевое различие — где и когда они выполняются.

+page.ts — универсальный загрузчик

  • Выполняется на сервере при SSR (первый запрос) и в браузере при клиентской навигации.
  • Имеет доступ к fetch (уже настроенному SvelteKit для правильной обработки cookie/credentials), params, url, route, data (от layout).
  • НЕ имеет доступа к locals, cookies, переменным из $env/static/private.
  • Подходит для запросов к публичным API без авторизации.
// src/routes/products/+page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ fetch, url }) => {
  const page = url.searchParams.get('page') ?? '1';
  // fetch здесь — это SvelteKit-обёртка, работающая и на сервере и в браузере
  const res = await fetch(`/api/products?page=${page}`);
  const products = await res.json();
  return { products };
};

+page.server.ts — серверный загрузчик

  • Выполняется только на сервере — при SSR и при каждой клиентской навигации (через внутренний fetch к серверу).
  • Имеет доступ к locals (пользователь из handle()), cookies, platform (Cloudflare Workers env), секретным переменным окружения.
  • Может напрямую обращаться к БД, файловой системе, внутренним сервисам.
  • Дополнительно экспортирует actions для обработки форм.
// src/routes/account/+page.server.ts
import { redirect, error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ locals, cookies }) => {
  // locals.user — установлен в hooks.server.ts
  if (!locals.user) {
    throw redirect(303, '/login');
  }

  // Прямой запрос к БД — возможен только здесь
  const orders = await db.orders.findByUser(locals.user.id);
  const sessionInfo = cookies.get('session');

  return { user: locals.user, orders };
};

Совместное использование

Оба файла могут существовать одновременно. Данные мёржатся: +page.ts получает data из серверного загрузчика через параметр parent().

// +page.server.ts
export const load = async ({ locals }) => {
  return { user: locals.user }; // Только сервер
};

// +page.ts
export const load = async ({ fetch, parent }) => {
  const { user } = await parent(); // Данные из server load
  const posts = await fetch('/api/posts').then(r => r.json());
  return { posts }; // Мёржится с { user }
};

Таблица сравнения

  • Где выполняется: .ts — сервер + браузер; .server.ts — только сервер
  • locals/cookies: .ts — нет; .server.ts — да
  • Прямой доступ к БД: .ts — нет; .server.ts — да
  • fetch к публичным API: .ts — да; .server.ts — да
  • Form actions: .ts — нет; .server.ts — да
  • Повторный вызов при навигации: .ts — в браузере; .server.ts — через сервер

Подводные камни

  • Если используется только +page.ts и в нём делается запрос к защищённому API, при клиентской навигации cookie может не передаться корректно — используйте +page.server.ts для чувствительных данных.
  • parent() в +page.ts ждёт завершения +page.server.ts и layout загрузчиков — это блокирует параллелизм; не вызывайте parent() если данные не нужны немедленно.
  • При наличии обоих файлов TypeScript тип PageData объединяет оба возвращаемых значения — убедитесь, что нет конфликтующих ключей.
  • В +page.server.ts данные сериализуются из сервера в браузер через devalue — Map, Set, Date поддерживаются, но кастомные классы — нет.
  • +page.ts с export const ssr = false будет выполняться только в браузере — это полезно для страниц с клиентским состоянием, но убивает SEO.
  • Функция fetch внутри +page.ts при SSR делает внутренний запрос к серверу SvelteKit — это накладные расходы; для серверных данных предпочитайте +page.server.ts с прямым вызовом.

Common mistakes

  • Путать load functions с похожим API из соседнего фреймворка.
  • Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
  • Игнорировать влияние на hydration, cache, bundle size или безопасность.

What the interviewer is testing

  • Точно объясняет назначение механизма «load functions».
  • Показывает корректный минимальный пример без выдуманных API.
  • Называет ограничения, failure modes и production-компромиссы.

Sources

Related topics