Перейти к содержимому

Поверхностная маршрутизация

Когда вы перемещаетесь по приложению SvelteKit, создаются записи истории. При нажатии кнопок «Назад» и «Вперёд» происходит перемещение по этому списку записей, при этом заново выполняются любые функции load и заменяются компоненты страницы по мере необходимости.

Иногда бывает полезно создавать записи истории без перехода на другую страницу. Например, вы можете захотеть показать модальное окно (диалог), которое пользователь сможет закрыть, нажав кнопку «Назад». Это особенно ценно на мобильных устройствах, где жесты смахивания (swipe) часто более естественны, чем прямое взаимодействие с интерфейсом. В таких случаях модальное окно, не связанное с записью истории, может вызывать раздражение: пользователь может смахнуть назад, пытаясь его закрыть, и в итоге окажется на неправильной странице.

SvelteKit делает это возможным с помощью функций pushState и replaceState. Они позволяют ассоциировать состояние с записью истории без перехода на другую страницу. Например, для реализации модального окна, управляемого через историю:

+page.svelte
<script>
import { pushState } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
function showModal() {
pushState('', {
showModal: true
});
}
</script>
{#if page.state.showModal}
<Modal close={() => history.back()} />
{/if}

Модальное окно можно закрыть, перейдя назад (сбросив page.state.showModal) или взаимодействуя с ним таким образом, чтобы выполнился колбэк close, который программно выполнит переход назад.

Первый аргумент pushState — это URL относительно текущего URL. Чтобы остаться на текущем URL, используйте ''.

Второй аргумент — новое состояние страницы, которое можно получить через объект page как page.state. Вы можете сделать состояние страницы типобезопасным, объявив интерфейс App.PageState (обычно в src/app.d.ts).

Чтобы установить состояние страницы без создания новой записи в истории, используйте replaceState вместо pushState.

При поверхностной маршрутизации вам может понадобиться отрисовать другой компонент +page.svelte внутри текущей страницы. Например, при клике на миниатюру фото может открыться детальный просмотр без перехода на страницу фото.

Для этого нужно загрузить данные, которые ожидает +page.svelte. Удобный способ — использовать preloadData внутри обработчика click элемента <a>. Если элемент (или его родитель) использует атрибут data-sveltekit-preload-data, данные уже будут запрошены, и preloadData повторно использует этот запрос.

src/routes/photos/+page.svelte
<script>
import { preloadData, pushState, goto } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
let { data } = $props();
</script>
{#each data.thumbnails as thumbnail}
<a
href="/photos/{thumbnail.id}"
onclick={async (e) => {
if (innerWidth < 640 // выходим, если экран слишком маленький
|| e.shiftKey // или ссылка открывается в новом окне
|| e.metaKey || e.ctrlKey // или в новой вкладке (mac: metaKey, win/linux: ctrlKey)
// также стоит учитывать клик колёсиком мыши
) return;
// предотвращаем навигацию
e.preventDefault();
const { href } = e.currentTarget;
// запускаем функции `load` (точнее, получаем результат функций `load`,
// которые уже выполняются благодаря `data-sveltekit-preload-data`)
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState(href, { selected: result.data });
} else {
// что-то пошло не так! попробуем перейти обычным способом
goto(href);
}
}}
>
<img alt={thumbnail.alt} src={thumbnail.src} />
</a>
{/each}
{#if page.state.selected}
<Modal onclose={() => history.back()}>
<!-- передаём данные страницы в компонент +page.svelte
так же, как это сделал бы SvelteKit при навигации -->
<PhotoPage data={page.state.selected} />
</Modal>
{/if}

Во время серверного рендеринга (SSR) page.state всегда является пустым объектом. То же самое относится к первой странице, на которую попадает пользователь — если пользователь перезагрузит страницу (или вернётся из другого документа), состояние не будет применено, пока он не выполнит навигацию.

Поверхностная маршрутизация — это функция, которая требует JavaScript для работы. Будьте внимательны при её использовании и старайтесь предусмотреть разумное резервное поведение на случай, если JavaScript недоступен.