Загрузка данных
Перед отрисовкой компонента +page.svelte (и содержащих его компонентов +layout.svelte) часто требуется получить данные. Это реализуется через функции load.
Данные страницы
Заголовок раздела «Данные страницы»Рядом с файлом +page.svelte может находиться +page.js, экспортирующий функцию load. Её возвращаемое значение доступно странице через проп data:
/** @type {import('./$types').PageLoad} */export function load({ params }) { return { post: { title: `Title for ${params.slug} goes here`, content: `Content for ${params.slug} goes here` } };}import type { PageLoad } from './$types';
export const load: PageLoad = ({ params }) => { return { post: { title: `Title for ${params.slug} goes here`, content: `Content for ${params.slug} goes here` } };};<script> /** @type {import('./$types').PageProps} */ let { data } = $props();</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div><script lang="ts"> import type { PageProps } from './$types';
let { data }: PageProps = $props();</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div>Благодаря сгенерированному модулю $types мы получаем полную типобезопасность.
Функция load в файле +page.js выполняется как на сервере, так и в браузере (если не используется export const ssr = false, в этом случае она выполняется только в браузере). Если ваша функция load должна всегда выполняться на сервере (например, потому что использует приватные переменные окружения или обращается к базе данных), её следует поместить в файл +page.server.js.
Более реалистичная версия функции load для поста в блоге, которая выполняется только на сервере и получает данные из базы, может выглядеть так:
import * as db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */export async function load({ params }) { return { post: await db.getPost(params.slug) };}import * as db from '$lib/server/database';import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => { return { post: await db.getPost(params.slug) };};Обратите внимание, что тип изменился с PageLoad на PageServerLoad, поскольку серверные функции load имеют доступ к дополнительным аргументам. Чтобы понять, когда использовать +page.js, а когда +page.server.js, см. раздел Универсальные и серверные.
Данные макета
Заголовок раздела «Данные макета»Ваши файлы +layout.svelte также могут загружать данные через +layout.js или +layout.server.js.
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */export async function load() { return { posts: await db.getPostSummaries() };}import * as db from '$lib/server/database';import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async () => { return { posts: await db.getPostSummaries() };};<script> /** @type {import('./$types').LayoutProps} */ let { data, children } = $props();</script>
<main> <!-- отображаем +page.svelte --> {@render children()}</main>
<aside> <h2>Больше постов</h2> <ul> {#each data.posts as post} <li> <a href="/blog/{post.slug}"> {post.title} </a> </li> {/each} </ul></aside><script lang="ts"> import type { LayoutProps } from './$types';
let { data, children }: LayoutProps = $props();</script>
<main> <!-- отображаем +page.svelte --> {@render children()}</main>
<aside> <h2>Больше постов</h2> <ul> {#each data.posts as post} <li> <a href="/blog/{post.slug}"> {post.title} </a> </li> {/each} </ul></aside>Данные, возвращаемые из функций load макета, доступны:
- Дочерним компонентам
+layout.svelte - Компоненту
+page.svelte - Самому макету, которому они «принадлежат»
<script> import { page } from '$app/state';
/** @type {import('./$types').PageProps} */ let { data } = $props();
// мы можем получить доступ к `data.posts`, так как эти данные возвращаются // из родительской функции `load` макета let index = $derived(data.posts.findIndex(post => post.slug === page.params.slug)); let next = $derived(data.posts[index + 1]);</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div>
{#if next} <p>Следующий пост: <a href="/blog/{next.slug}">{next.title}</a></p>{/if}<script lang="ts"> import { page } from '$app/state'; import type { PageProps } from './$types';
let { data }: PageProps = $props();
// мы можем получить доступ к `data.posts`, так как эти данные возвращаются // из родительской функции `load` макета let index = $derived(data.posts.findIndex(post => post.slug === page.params.slug)); let next = $derived(data.posts[index + 1]);</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div>
{#if next} <p>Следующий пост: <a href="/blog/{next.slug}">{next.title}</a></p>{/if}page.data
Заголовок раздела «page.data»Компонент +page.svelte и каждый родительский компонент +layout.svelte имеют доступ к своим данным и всем данным своих родителей.
В некоторых случаях может потребоваться обратное — родительскому макету может понадобиться доступ к данным страницы или дочернего макета. Например, корневой макет может захотеть получить доступ к свойству title, возвращаемому функцией load из +page.js или +page.server.js. Это можно сделать через page.data:
<script> import { page } from '$app/state';</script>
<svelte:head> <title>{page.data.title}</title></svelte:head><script lang="ts"> import { page } from '$app/state';</script>
<svelte:head> <title>{page.data.title}</title></svelte:head>Тип для page.data предоставляется через App.PageData.
Универсальные и серверные
Заголовок раздела «Универсальные и серверные»Как мы видели, существует два типа функций load:
- Файлы
+page.jsи+layout.jsэкспортируют универсальные функцииload, выполняемые и на сервере, и в браузере - Файлы
+page.server.jsи+layout.server.jsэкспортируют серверные функцииload, выполняемые только на стороне сервера
Концептуально они представляют одно и то же, но есть несколько важных отличий, о которых следует знать.
Когда какая функция load выполняется?
Заголовок раздела «Когда какая функция load выполняется?»Серверные функции load всегда выполняются на сервере.
По умолчанию универсальные функции load выполняются:
- На сервере во время SSR при первом посещении страницы
- Затем в браузере во время гидратации (с повторным использованием ответов fetch-запросов)
- Все последующие вызовы — исключительно в браузере
Поведение можно настроить через параметры страницы. При отключении SSR универсальные load функции всегда выполняются на клиенте.
Если маршрут содержит и универсальные, и серверные load функции, серверная выполняется первой.
Функция load вызывается во время выполнения, кроме случаев пререндеринга — тогда она вызывается при сборке.
Входные параметры
Заголовок раздела «Входные параметры»Оба типа load функций получают доступ к:
- Свойствам запроса (
params,route,url) - Функциям (
fetch,setHeaders,parent,depends,untrack)
Серверные load получают ServerLoadEvent с:
clientAddress,cookies,locals,platform,request(изRequestEvent)
Универсальные load получают LoadEvent с:
- Свойством
data(результат сервернойload, если она есть)
Возвращаемые значения
Заголовок раздела «Возвращаемые значения»Универсальная load может вернуть любой объект, включая:
- Пользовательские классы
- Конструкторы компонентов
Серверная load должна возвращать данные, сериализуемые через devalue:
- JSON-совместимые структуры
BigInt,Date,Map,Set,RegExp- Циклические ссылки
- Промисы (потоковая передача)
Когда что использовать
Заголовок раздела «Когда что использовать»Серверные функции load удобны, когда нужно:
- Получить данные напрямую из базы данных или файловой системы
- Использовать приватные переменные окружения
Универсальные функции load полезны, когда нужно:
- Загружать данные через
fetchиз внешнего API (без приватных данных) - Возвращать несериализуемые объекты (например, конструкторы Svelte-компонентов)
В редких случаях может потребоваться использовать оба типа вместе — например, когда нужно вернуть экземпляр кастомного класса, инициализированного серверными данными. При совместном использовании результат серверной load передаётся не напрямую на страницу, а в универсальную load функцию (через свойство data):
/** @type {import('./$types').PageServerLoad} */export async function load() { return { serverMessage: 'привет от серверной функции `load`' };}import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async () => { return { serverMessage: 'привет от серверной функции `load`' };};/** @type {import('./$types').PageLoad} */export async function load({ data }) { return { serverMessage: data.serverMessage, universalMessage: 'привет от универсальной функции `load`' };}import type { PageLoad } from './$types';export const load: PageLoad = async ({ data }) => { return { serverMessage: data.serverMessage, universalMessage: 'привет от универсальной функции `load`' };};Использование данных URL
Заголовок раздела «Использование данных URL»Часто функция load так или иначе зависит от URL. Для этого функция load предоставляет доступ к url, route и params.
Экземпляр URL, содержащий свойства:
originhostnamepathnamesearchParams(разобранная query-строка какURLSearchParams)
url.hash недоступен во время load на сервере.
Содержит имя текущей директории маршрута относительно src/routes:
/** @type {import('./$types').PageLoad} */export function load({ route }) { console.log(route.id); // '/a/[b]/[...c]'}import type { PageLoad } from './$types';
export const load: PageLoad = ({ route }) => { console.log(route.id); // '/a/[b]/[...c]'};Объект params формируется на основе url.pathname и route.id.
Для примера, при route.id равном /a/[b]/[...c] и url.pathname равном /a/x/y/z, объект params будет выглядеть так:
{ "b": "x", "c": "y/z"}Выполнение fetch-запросов
Заголовок раздела «Выполнение fetch-запросов»Для получения данных из внешнего API или обработчика +server.js можно использовать предоставляемую функцию fetch, которая работает аналогично нативному fetch API с некоторыми дополнительными возможностями:
- Позволяет выполнять авторизованные запросы на сервере, наследуя заголовки
cookieиauthorizationиз исходного запроса страницы. - Поддерживает относительные URL на сервере (обычно
fetchтребует указания origin в серверном контексте). - Внутренние запросы (например к маршрутам
+server.js) выполняются напрямую на сервере без HTTP-вызова. - При SSR ответы автоматически встраиваются в HTML через перехват методов
text,jsonиarrayBufferобъектаResponse. Заголовки не сериализуются, если явно не указано вfilterSerializedResponseHeaders. - При гидратации данные читаются из HTML, обеспечивая согласованность и избегая повторных запросов (консольные предупреждения при использовании браузерного
fetchвместоloadfetchсвязаны именно с этим).
/** @type {import('./$types').PageLoad} */export async function load({ fetch, params }) { const res = await fetch(`/api/items/${params.id}`); const item = await res.json();
return { item };}import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch, params }) => { const res = await fetch(`/api/items/${params.id}`); const item = await res.json();
return { item };};Серверная функция load может получать и устанавливать куки.
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */export async function load({ cookies }) { const sessionid = cookies.get('sessionid');
return { user: await db.getUser(sessionid) };}import * as db from '$lib/server/database';import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ cookies }) => { const sessionid = cookies.get('sessionid');
return { user: await db.getUser(sessionid) };};Куки будут передаваться через предоставленную функцию fetch только если целевой хост совпадает с доменом приложения SvelteKit или является его поддоменом.
Например, если SvelteKit обслуживает my.domain.com:
- domain.com НЕ получит куки
- my.domain.com получит куки
- api.domain.com НЕ получит куки
- sub.my.domain.com получит куки
Другие куки не будут передаваться при credentials: 'include', потому что SvelteKit не знает, к какому домену относится каждый куки (браузер не передаёт эту информацию), поэтому их передача небезопасна. Используйте хук handleFetch для обхода этого ограничения.
Заголовки
Заголовок раздела «Заголовки»Как серверные, так и универсальные функции load имеют доступ к функции setHeaders, которая на сервере позволяет устанавливать заголовки ответа. (В браузере setHeaders не имеет эффекта.) Это полезно, например, для кэширования страницы:
/** @type {import('./$types').PageLoad} */export async function load({ fetch, setHeaders }) { const url = `https://cms.example.com/products.json`; const response = await fetch(url);
// Заголовки устанавливаются только во время SSR, кэшируя HTML страницы // на тот же срок, что и исходные данные. setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') });
return response.json();}import type { PageLoad } from './$types';export const load: PageLoad = async ({ fetch, setHeaders }) => { const url = `https://cms.example.com/products.json`; const response = await fetch(url);
// Заголовки устанавливаются только во время SSR, кэшируя HTML страницы // на тот же срок, что и исходные данные. setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') });
return response.json();};Установка одного и того же заголовка несколько раз (даже в разных функциях load) вызовет ошибку. Каждый заголовок можно установить только один раз через setHeaders. Нельзя добавлять заголовок set-cookie через setHeaders — вместо этого используйте cookies.set(name, value, options).
Использование родительских данных
Заголовок раздела «Использование родительских данных»Иногда функции load требуется доступ к данным из родительской функции load, что можно сделать через await parent():
/** @type {import('./$types').LayoutLoad} */export function load() { return { a: 1 };}import type { LayoutLoad } from './$types';
export const load: LayoutLoad = () => { return { a: 1 };};/** @type {import('./$types').LayoutLoad} */export async function load({ parent }) { const { a } = await parent(); return { b: a + 1 };}import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async ({ parent }) => { const { a } = await parent(); return { b: a + 1 };};/** @type {import('./$types').PageLoad} */export async function load({ parent }) { const { a, b } = await parent(); return { c: a + b };}import type { PageLoad } from './$types';
export const load: PageLoad = async ({ parent }) => { const { a, b } = await parent(); return { c: a + b };};<script> /** @type {import('./$types').PageProps} */ let { data } = $props();</script>
<!-- отобразит `1 + 2 = 3` --><p>{data.a} + {data.b} = {data.c}</p><script lang="ts"> import type { PageProps } from './$types';
let { data }: PageProps = $props();</script>
<!-- отобразит `1 + 2 = 3` --><p>{data.a} + {data.b} = {data.c}</p>В +page.server.js и +layout.server.js функция parent возвращает данные из родительских файлов +layout.server.js.
В +page.js или +layout.js она возвращает данные из родительских +layout.js. Однако отсутствующий +layout.js обрабатывается как функция ({ data }) => data, что означает также возврат данных из родительских +layout.server.js, которые не «перекрыты» файлом +layout.js.
Старайтесь избегать каскада запросов при использовании await parent(). Например, здесь getData(params) не зависит от результата вызова parent(), поэтому следует вызывать её первой, чтобы не задерживать рендеринг:
/** @type {import('./$types').PageLoad} */export async function load({ params, parent }) { const parentData = await parent(); const data = await getData(params); const parentData = await parent();
return { ...data, meta: { ...parentData.meta, ...data.meta } };}import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, parent }) => { const parentData = await parent(); const data = await getData(params); const parentData = await parent();
return { ...data, meta: { ...parentData.meta, ...data.meta } };};Если во время выполнения load произойдёт ошибка, будет отображён ближайший +error.svelte. Для ожидаемых ошибок используйте хелпер error из @sveltejs/kit, чтобы указать HTTP-статус и опциональное сообщение:
import { error } from '@sveltejs/kit';
/** @type {import('./$types').LayoutServerLoad} */export function load({ locals }) { if (!locals.user) { error(401, 'not logged in'); }
if (!locals.user.isAdmin) { error(403, 'not an admin'); }}import { error } from '@sveltejs/kit';import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = ({ locals }) => { if (!locals.user) { error(401, 'not logged in'); }
if (!locals.user.isAdmin) { error(403, 'not an admin'); }};Вызов error(...) генерирует исключение, что позволяет легко прервать выполнение внутри вспомогательных функций.
При возникновении неожиданной ошибки SvelteKit вызовет handleError и обработает её как 500 Internal Error.
Перенаправления
Заголовок раздела «Перенаправления»Для редиректа пользователей используйте хелпер redirect из @sveltejs/kit, указав целевой URL и статус-код 3xx. Как и error(...), вызов redirect(...) генерирует исключение, что позволяет легко прервать выполнение внутри вспомогательных функций.
import { redirect } from '@sveltejs/kit';
/** @type {import('./$types').LayoutServerLoad} */export function load({ locals }) { if (!locals.user) { redirect(307, '/login'); }}import { redirect } from '@sveltejs/kit';import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = ({ locals }) => { if (!locals.user) { redirect(307, '/login'); }};В браузере вы также можете программно выполнить навигацию вне функции load, используя goto из $app.navigation.
Потоковая передача промисов
Заголовок раздела «Потоковая передача промисов»При использовании серверной функции load промисы будут передаваться в браузер по мере их выполнения. Это особенно полезно для медленных или не критичных данных, так как позволяет начать отрисовку страницы до получения всех данных:
/** @type {import('./$types').PageServerLoad} */export async function load({ params }) { return { // Убедитесь, что `await` выполняется в конце, иначе // мы не сможем начать загрузку комментариев до загрузки поста comments: loadComments(params.slug), post: await loadPost(params.slug) };}import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => { return { // Убедитесь, что `await` выполняется в конце, иначе // мы не сможем начать загрузку комментариев до загрузки поста comments: loadComments(params.slug), post: await loadPost(params.slug) };};Это полезно, например, для создания состояний загрузки со скелетоном:
<script> /** @type {import('./$types').PageProps} */ let { data } = $props();</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div>
{#await data.comments} Загрузка комментариев...{:then comments} {#each comments as comment} <p>{comment.content}</p> {/each}{:catch error} <p>Ошибка загрузки комментариев: {error.message}</p>{/await}<script lang="ts"> import type { PageProps } from './$types';
let { data }: PageProps = $props();</script>
<h1>{data.post.title}</h1><div>{@html data.post.content}</div>
{#await data.comments} Загрузка комментариев...{:then comments} {#each comments as comment} <p>{comment.content}</p> {/each}{:catch error} <p>Ошибка загрузки комментариев: {error.message}</p>{/await}При потоковой передаче данных важно правильно обрабатывать отклонённые промисы. В частности, сервер может завершиться с ошибкой unhandled promise rejection, если лениво загружаемый промис завершится ошибкой до начала рендеринга (когда ошибка ещё не перехвачена) и ошибка никак не обрабатывается. При использовании fetch SvelteKit непосредственно в функции load, SvelteKit позаботится об этом случае. Для других промисов достаточно прикрепить catch-обработчик без действий (noop), чтобы пометить промис как обработанный.
/** @type {import('./$types').PageServerLoad} */export function load({ fetch }) { const ok_manual = Promise.reject(); ok_manual.catch(() => {});
return { ok_manual, ok_fetch: fetch('/fetch/that/could/fail'), dangerous_unhandled: Promise.reject() };}import type { PageServerLoad } from './$types';
export const load: PageServerLoad = ({ fetch }) => { const ok_manual = Promise.reject(); ok_manual.catch(() => {});
return { ok_manual, ok_fetch: fetch('/fetch/that/could/fail'), dangerous_unhandled: Promise.reject() };};Параллельная загрузка
Заголовок раздела «Параллельная загрузка»При рендеринге страницы (или переходе на нее) SvelteKit выполняет все функции load параллельно, избегая каскада запросов. При клиентской навигации результаты вызовов серверных функций load объединяются в один ответ. После завершения всех функций load страница отрисовывается.
Повторный запуск функций load
Заголовок раздела «Повторный запуск функций load»SvelteKit отслеживает зависимости каждой функции load, чтобы избежать её избыточного выполнения при навигации.
Например, для пары функций load вида…
import * as db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */export async function load({ params }) { return { post: await db.getPost(params.slug) };}import * as db from '$lib/server/database';import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => { return { post: await db.getPost(params.slug) };};import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */export async function load() { return { posts: await db.getPostSummaries() };}import * as db from '$lib/server/database';import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async () => { return { posts: await db.getPostSummaries() };};…функция в +page.server.js перезапустится при переходе с /blog/trying-the-raw-meat-diet на /blog/i-regret-my-choices, так как изменился params.slug. Функция в +layout.server.js не перезапустится, так как данные остаются актуальными. Другими словами, db.getPostSummaries() не будет вызван повторно.
Функция load, вызывающая await parent(), также перезапустится, если перезапустится родительская функция load.
Отслеживание зависимостей не применяется после возврата из функции load — например, обращение к params.x внутри вложенного промиса не вызовет перезапуск функции при изменении params.x. (Не беспокойтесь, в режиме разработки вы получите предупреждение, если случайно сделаете это.) Вместо этого обращайтесь к параметру в основном теле функции load.
Параметры поиска отслеживаются независимо от остальной части URL. Например, обращение к event.url.searchParams.get("x") в функции load вызовет её перезапуск при переходе с ?x=1 на ?x=2, но не при переходе с ?x=1&y=1 на ?x=1&y=2.
Исключение зависимостей из отслеживания
Заголовок раздела «Исключение зависимостей из отслеживания»В редких случаях может потребоваться исключить что-то из механизма отслеживания зависимостей. Это можно сделать с помощью предоставленной функции untrack:
/** @type {import('./$types').PageLoad} */export async function load({ untrack, url }) { // Исключаем url.pathname из отслеживания, чтобы изменения пути не вызывали повторный запуск if (untrack(() => url.pathname === '/')) { return { message: 'Welcome!' }; }}import type { PageLoad } from './$types';
export const load: PageLoad = async ({ untrack, url }) => { // Исключаем url.pathname из отслеживания, чтобы изменения пути не вызывали повторный запуск if (untrack(() => url.pathname === '/')) { return { message: 'Welcome!' }; }};Ручной перезапуск
Заголовок раздела «Ручной перезапуск»Вы также можете перезапустить функции load для текущей страницы с помощью:
invalidate(url)— перезапускает всеloadфункции, зависящие отurlinvalidateAll()— перезапускает всеloadфункции
Серверные load функции никогда не зависят автоматически от полученного url, чтобы избежать утечки секретов клиенту.
Функция load зависит от url, если вызывает:
fetch(url)depends(url)
Примечание: url может быть кастомным идентификатором, начинающимся с [a-z]::
/** @type {import('./$types').PageLoad} */export async function load({ fetch, depends }) { // Функция load перезапустится при вызове `invalidate('https://api.example.com/random-number') const response = await fetch('https://api.example.com/random-number');
// ...или при вызове `invalidate('app:random')` depends('app:random');
return { number: await response.json() };}import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch, depends }) => { // Функция load перезапустится при вызове `invalidate('https://api.example.com/random-number') const response = await fetch('https://api.example.com/random-number');
// ...или при вызове `invalidate('app:random')` depends('app:random');
return { number: await response.json() };};<script> import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageProps} */ let { data } = $props();
function rerunLoadFunction() { // Любое из этих действий приведёт к повторному выполнению функции `load` invalidate('app:random'); invalidate('https://api.example.com/random-number'); invalidate(url => url.href.includes('random-number')); invalidateAll(); }</script>
<p>Случайное число: {data.number}</p><button onclick={rerunLoadFunction}>Обновить случайное число</button><script lang="ts"> import { invalidate, invalidateAll } from '$app/navigation'; import type { PageProps } from './$types';
let { data }: PageProps = $props();
function rerunLoadFunction() { // Любое из этих действий приведёт к повторному выполнению функции `load` invalidate('app:random'); invalidate('https://api.example.com/random-number'); invalidate(url => url.href.includes('random-number')); invalidateAll(); }</script>
<p>Случайное число: {data.number}</p><button onclick={rerunLoadFunction}>Обновить случайное число</button>Когда функции load выполняются повторно?
Заголовок раздела «Когда функции load выполняются повторно?»Подведём итоги: функция load выполнится повторно в следующих случаях:
- Она ссылается на свойство
params, значение которого изменилось - Она ссылается на свойство
url(например,url.pathnameилиurl.search), значение которого изменилось. Свойстваrequest.urlне отслеживаются - Она вызывает
url.searchParams.get(...),url.searchParams.getAll(...)илиurl.searchParams.has(...), и соответствующий параметр изменился. Доступ к другим свойствамurl.searchParamsимеет тот же эффект, что и доступ кurl.search. - Она вызывает
await parent(), и родительская функцияloadвыполнилась повторно - Дочерняя функция
loadвызываетawait parent()и выполняется повторно, а родитель является серверной функцией load - Она объявила зависимость от конкретного URL через
fetch(только универсальные load) илиdepends, и этот URL был помечен как недействительный с помощьюinvalidate(url) - Все активные функции
loadбыли принудительно перезапущены с помощьюinvalidateAll()
Изменения params и url могут происходить в результате:
Примечание: повторное выполнение функции load обновит проп data в соответствующем +layout.svelte или +page.svelte, но не приведёт к повторному созданию компонента. Таким образом, внутреннее состояние сохраняется. Если это нежелательно, можно сбросить состояние в колбэке afterNavigate и/или обернуть компонент в блок {#key ...}.
Влияние на аутентификацию
Заголовок раздела «Влияние на аутентификацию»Две особенности загрузки данных важны для проверки авторизации:
- Функции
loadмакетов не выполняются при каждом запросе (например, при клиентской навигации между дочерними маршрутами) (Когда функции load выполняются повторно?) - Функции
loadмакетов и страниц выполняются параллельно, если не вызванawait parent(). Если функцияloadмакета завершится ошибкой, функцияloadстраницы всё равно выполнится, но клиент не получит возвращённых данных
Возможные стратегии для гарантии проверки авторизации перед защищённым кодом:
Для предотвращения каскада запросов и сохранения кэша макетов:
- Используйте хуки для защиты маршрутов до выполнения любых функций
load - Используйте проверки авторизации непосредственно в функциях
loadфайлов+page.server.jsдля защиты конкретных маршрутов
Размещение проверки в +layout.server.js требует:
- Чтобы все дочерние страницы вызывали
await parent()перед защищённым кодом - Если не все дочерние страницы зависят от данных из
await parent(), другие варианты будут более производительными
Использование getRequestEvent
Заголовок раздела «Использование getRequestEvent»При выполнении серверных функций load объект event, передаваемый функции в качестве аргумента, также можно получить с помощью getRequestEvent. Это позволяет общей логике (например, проверкам авторизации) получать информацию о текущем запросе без необходимости её явной передачи.
Например, у вас может быть функция, требующая авторизации пользователя и перенаправляющая на /login, если пользователь не авторизован:
import { redirect } from '@sveltejs/kit';import { getRequestEvent } from '$app/server';
export function requireLogin() { const { locals, url } = getRequestEvent();
// предполагается, что `locals.user` заполняется в `handle` if (!locals.user) { const redirectTo = url.pathname + url.search; const params = new URLSearchParams({ redirectTo });
redirect(307, `/login?${params}`); }
return locals.user;}Теперь вы можете вызывать requireLogin в любой функции load (или, например, в действиях формы), чтобы гарантировать, что пользователь авторизован:
import { requireLogin } from '$lib/server/auth';
export function load() { const user = requireLogin();
// Здесь `user` гарантированно является объектом пользователя, так как // в противном случае `requireLogin` выполнил бы редирект и мы бы сюда не попали return { message: `hello ${user.name}!` };}