Расширенный роутинг
Остаточные параметры
Заголовок раздела «Остаточные параметры»Если количество сегментов маршрута неизвестно, вы можете использовать синтаксис rest — например, можно реализовать просмотрщик файлов GitHub следующим образом:
/[org]/[repo]/tree/[branch]/[...file]…в этом случае запрос к /sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md приведет к тому, что на странице будут доступны следующие параметры:
{ org: 'sveltejs', repo: 'kit', branch: 'main', file: 'documentation/docs/04-advanced-routing.md'}Страницы 404
Заголовок раздела «Страницы 404»Остаточные параметры также позволяют отображать пользовательские страницы 404. Учитывая эти маршруты:
src/routes/├ marx-brothers/│ ├ chico/│ ├ harpo/│ ├ groucho/│ └ +error.svelte└ +error.svelte…файл marx-brothers/+error.svelte не будет отображаться, если вы посетите /marx-brothers/karl, потому что ни один маршрут не был совпадающим. Если вы хотите отобразить вложенную страницу ошибки, вы должны создать маршрут, который соответствует любому запросу /marx-brothers/*, и вернуть из него 404:
src/routes/├ marx-brothers/| ├ [...path]/│ ├ chico/│ ├ harpo/│ ├ groucho/│ └ +error.svelte└ +error.svelteimport { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */export function load(event) { error(404, 'Not Found');}import { error } from '@sveltejs/kit';import type { PageLoad } from './$types';
export const load: PageLoad = (event) => { error(404, 'Not Found');};Опциональные параметры
Заголовок раздела «Опциональные параметры»Маршрут вроде [lang]/home содержит параметр с именем lang, который является обязательным. Иногда полезно сделать эти параметры опциональными, чтобы в этом примере как home, так и en/home указывали на одну и ту же страницу. Вы можете сделать это, обернув параметр в ещё одну пару скобок: [[lang]]/home
Обратите внимание, что опциональный параметр маршрута не может следовать за остаточным параметром ([...rest]/[[optional]]), поскольку параметры сопоставляются жадно, и опциональный параметр всегда будет неиспользованным.
Сопоставление
Заголовок раздела «Сопоставление»Маршрут вроде src/routes/fruits/[page] будет соответствовать /fruits/apple, но он также будет соответствовать /fruits/rocketship. Мы этого не хотим. Вы можете убедиться, что параметры маршрута правильно сформированы, добавив матчер («сопоставитель») — который принимает строковое значение параметра ("apple" или "rocketship") и возвращает true, если оно допустимо — в ваш каталог params:
/** * @param {string} param * @return {param is ('apple' | 'orange')} * @satisfies {import('@sveltejs/kit').ParamMatcher} */export function match(param) { return param === 'apple' || param === 'orange';}import type { ParamMatcher } from '@sveltejs/kit';
export const match = ((param: string): param is ('apple' | 'orange') => { return param === 'apple' || param === 'orange';}) satisfies ParamMatcher;…и расширив ваши маршруты:
src/routes/fruits/[page=fruit]Если путь не соответствует, SvelteKit попытается сопоставить другие маршруты (используя порядок сортировки, указанный ниже), прежде чем в итоге вернуть 404.
Каждый модуль в каталоге params соответствует матчеру, за исключением файлов *.test.js и *.spec.js, которые могут использоваться для юнит-тестирования ваших матчеров.
Сортировка
Заголовок раздела «Сортировка»Возможно, что несколько маршрутов соответствуют заданному пути. Например, каждый из этих маршрутов будет соответствовать /foo-abc:
src/routes/[...catchall]/+page.sveltesrc/routes/[[a=x]]/+page.sveltesrc/routes/[b]/+page.sveltesrc/routes/foo-[c]/+page.sveltesrc/routes/foo-abc/+page.svelteSvelteKit нужно знать, какой маршрут запрашивается. Для этого он сортирует их в соответствии со следующими правилами…
- Более конкретные маршруты имеют более высокий приоритет (например, маршрут без параметров более конкретный, чем маршрут с одним динамическим параметром, и так далее)
- Параметры с матчерами (
[name=type]) имеют более высокий приоритет, чем те без них ([name]) - Параметры
[[optional]]и[...rest]игнорируются, если они не являются последней частью маршрута, в этом случае они обрабатываются с наименьшим приоритетом. Другими словами,x/[[y]]/zобрабатывается эквивалентноx/zдля целей сортировки - Ничьи разрешаются в алфавитном порядке
…в результате получается такой порядок, что /foo-abc вызовет src/routes/foo-abc/+page.svelte, а /foo-def вызовет src/routes/foo-[c]/+page.svelte вместо менее конкретных маршрутов:
src/routes/foo-abc/+page.sveltesrc/routes/foo-[c]/+page.sveltesrc/routes/[[a=x]]/+page.sveltesrc/routes/[b]/+page.sveltesrc/routes/[...catchall]/+page.svelteКодирование
Заголовок раздела «Кодирование»Некоторые символы нельзя использовать в файловой системе — / в Linux и Mac, \ / : * ? " < > | в Windows. Символы # и % имеют специальное значение в URL, а символы [ ] ( ) имеют специальное значение в SvelteKit, поэтому их также нельзя использовать напрямую как часть маршрута.
Чтобы использовать эти символы в ваших маршрутах, вы можете применять шестнадцатеричные escape-последовательности, которые имеют формат [x+nn], где nn — это шестнадцатеричный код символа:
\—[x+5c]/—[x+2f]:—[x+3a]*—[x+2a]?—[x+3f]"—[x+22]<—[x+3c]>—[x+3e]|—[x+7c]#—[x+23]%—[x+25][—[x+5b]]—[x+5d](—[x+28])—[x+29]
Например, чтобы создать маршрут /smileys/:-), вы создадите файл src/routes/smileys/[x+3a]-[x+29]/+page.svelte.
Вы можете определить шестнадцатеричный код символа с помощью JavaScript:
':'.charCodeAt(0).toString(16); // '3a', hence '[x+3a]'Вы также можете использовать escape-последовательности Unicode. Обычно в этом нет необходимости, поскольку можно использовать незакодированный символ напрямую, но если — по какой-то причине — вы не можете иметь имя файла с эмодзи, например, то используйте escaped-символы. Другими словами, эти варианты эквивалентны:
src/routes/[u+d83e][u+dd2a]/+page.sveltesrc/routes/🤪/+page.svelteФормат escape-последовательности Unicode — [u+nnnn], где nnnn — допустимое значение от 0000 до 10ffff. (В отличие от экранирования строк в JavaScript, нет необходимости использовать суррогатные пары для представления кодовых точек выше ffff.) Чтобы узнать больше о кодировках Unicode, обратитесь к статье Programming with Unicode.
Расширенные макеты
Заголовок раздела «Расширенные макеты»По умолчанию иерархия макетов отражает иерархию маршрутов. В некоторых случаях это может быть не тем, чего вы хотите.
(group)
Заголовок раздела «(group)»Возможно, у вас есть некоторые маршруты, которые являются ‘app’-маршрутами и должны иметь один макет (например, /dashboard или /item), а другие — ‘marketing’-маршруты, которые должны иметь другой макет (/about или /testimonials). Мы можем группировать эти маршруты с помощью каталога, имя которого заключено в скобки — в отличие от обычных каталогов, (app) и (marketing) не влияют на pathname URL маршрутов внутри них:
src/routes/│ (app)/│ ├ dashboard/│ ├ item/│ └ +layout.svelte│ (marketing)/│ ├ about/│ ├ testimonials/│ └ +layout.svelte├ admin/└ +layout.svelteВы также можете поместить +page напрямую внутри (group), например, если / должна быть страницей (app) или (marketing).
Выход из макетов
Заголовок раздела «Выход из макетов»Корневой макет применяется ко всем страницам вашего приложения — если он опущен, по умолчанию используется {@render children()}. Если вы хотите, чтобы некоторые страницы имели другую иерархию макетов, отличную от остальных, то вы можете поместить всё приложение внутрь одной или нескольких групп за исключением маршрутов, которые не должны наследовать общие макеты.
В примере выше маршрут /admin не наследует ни макет (app), ни (marketing).
Страницы могут выходить из текущей иерархии макетов на основе маршрута за маршрутом. Предположим, у нас есть маршрут /item/[id]/embed внутри группы (app) из предыдущего примера:
src/routes/├ (app)/│ ├ item/│ │ ├ [id]/│ │ │ ├ embed/│ │ │ │ └ +page.svelte│ │ │ └ +layout.svelte│ │ └ +layout.svelte│ └ +layout.svelte└ +layout.svelteОбычно это наследовало бы корневой макет, макет (app), макет item и макет [id]. Мы можем сбросить до одного из этих макетов, добавив @, за которым следует имя сегмента — или, для корневого макета, пустую строку. В этом примере мы можем выбрать из следующих вариантов:
+page@[id].svelte- inherits fromsrc/routes/(app)/item/[id]/+layout.svelte+page@item.svelte- inherits fromsrc/routes/(app)/item/+layout.svelte+page@(app).svelte- inherits fromsrc/routes/(app)/+layout.svelte+page@.svelte- inherits fromsrc/routes/+layout.svelte
src/routes/├ (app)/│ ├ item/│ │ ├ [id]/│ │ │ ├ embed/│ │ │ │ └ +page@(app).svelte│ │ │ └ +layout.svelte│ │ └ +layout.svelte│ └ +layout.svelte└ +layout.svelte+layout@
Заголовок раздела «+layout@»Как и страницы, макеты могут сами выходить из иерархии родительского макета, используя ту же технику. Например, компонент +layout@.svelte сбросит иерархию для всех своих дочерних маршрутов.
src/routes/├ (app)/│ ├ item/│ │ ├ [id]/│ │ │ ├ embed/│ │ │ │ └ +page.svelte // использует (app)/item/[id]/+layout.svelte│ │ │ ├ +layout.svelte // наследует от (app)/item/+layout@.svelte│ │ │ └ +page.svelte // использует (app)/item/+layout@.svelte│ │ └ +layout@.svelte // наследует от корневого макета, пропуская (app)/+layout.svelte│ └ +layout.svelte└ +layout.svelteКогда использовать группы макетов
Заголовок раздела «Когда использовать группы макетов»Не все случаи использования подходят для группировки макетов, и вы не обязаны их применять. Возможно, ваш случай приведёт к сложной вложенности (group), или вы не хотите вводить (group) для одного исключения. Совершенно нормально использовать другие средства, такие как композиция (переиспользуемые функции load или компоненты Svelte) или условные операторы, чтобы добиться желаемого. В следующем примере показан макет, возвращающийся к корневому макету и переиспользующий компоненты и функции, которые могут использовать и другие макеты:
// src/routes/nested/route/+layout@.svelte<script> import ReusableLayout from '$lib/ReusableLayout.svelte'; let { data, children } = $props();</script>
<ReusableLayout {data}> {@render children()}</ReusableLayout>// src/routes/nested/route/+layout@.svelte<script lang="ts"> import ReusableLayout from '$lib/ReusableLayout.svelte'; let { data, children } = $props();</script>
<ReusableLayout {data}> {@render children()}</ReusableLayout>import { reusableLoad } from '$lib/reusable-load-function';
/** @type {import('./$types').PageLoad} */export function load(event) { // Добавьте здесь дополнительную логику, если нужно return reusableLoad(event);}import { reusableLoad } from '$lib/reusable-load-function';import type { PageLoad } from './$types';
export const load: PageLoad = (event) => { // Добавьте здесь дополнительную логику, если нужно return reusableLoad(event);};