高级
浅层路由
在 GitHub 上编辑此页面在 SvelteKit 应用中导航时,您会创建历史记录条目。单击后退和前进按钮将遍历此条目列表,重新运行任何 load
函数,并根据需要替换页面组件。
有时,创建不进行导航的历史记录条目非常有用。例如,您可能希望显示一个模态对话框,用户可以通过导航返回来关闭该对话框。这在移动设备上尤其有价值,因为滑动操作通常比直接与 UI 交互更自然。在这些情况下,不与历史记录条目关联的模态对话框可能会令人沮丧,因为用户可能会向后滑动以尝试关闭它,结果却发现自己进入了错误的页面。
SvelteKit 通过 pushState
和 replaceState
函数实现了这一点,这些函数允许您将状态与历史记录条目关联,而无需进行导航。例如,要实现一个历史记录驱动的模态对话框
<script>
import { pushState } from '$app/navigation';
import { page } from '$app/stores';
import Modal from './Modal.svelte';
function showModal() {
pushState('', {
showModal: true
});
}
</script>
{#if $page.state.showModal}
<Modal close={() => history.back()} />
{/if}
<script lang="ts">
import { pushState } from '$app/navigation';
import { page } from '$app/stores';
import Modal from './Modal.svelte';
function showModal() {
pushState('', {
showModal: true,
});
}
</script>
{#if $page.state.showModal}
<Modal close={() => history.back()} />
{/if}
可以通过导航返回(取消设置 $page.state.showModal
)或通过与它交互来关闭模态对话框,这种交互会导致 close
回调运行,从而以编程方式导航返回。
API永久链接
pushState
的第一个参数是相对于当前 URL 的 URL。要停留在当前 URL,请使用 ''
。
第二个参数是新页面状态,可以通过 页面存储 作为 $page.state
访问。你可以通过声明一个 App.
PageState
接口(通常在 src/app.d.ts
中)来使页面状态类型安全。
要设置页面状态而不创建新的历史记录项,请使用 replaceState
而不是 pushState
。
为路由加载数据永久链接
在浅层路由时,你可能希望在当前页面内渲染另一个 +page.svelte
。例如,点击照片缩略图可以在不导航到照片页面的情况下弹出详细视图。
为此,你需要加载 +page.svelte
预期的数据。一种便捷的方法是在 <a>
元素的 click
处理程序中使用 preloadData
。如果元素(或父元素)使用 data-sveltekit-preload-data
,则数据将已被请求,并且 preloadData
将重用该请求。
<script>
import { preloadData, pushState, goto } from '$app/navigation';
import { page } from '$app/stores';
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
export let data;
</script>
{#each data.thumbnails as thumbnail}
<a
href="/photos/{thumbnail.id}"
on:click={async (e) => {
// bail if opening a new tab, or we're on too small a screen
if (e.metaKey || innerWidth < 640) return;
// prevent navigation
e.preventDefault();
const { href } = e.currentTarget;
// run `load` functions (or rather, get the result of the `load` functions
// that are already running because of `data-sveltekit-preload-data`)
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState(href, { selected: result.data });
} else {
// something bad happened! try navigating
goto(href);
}
}}
>
<img alt={thumbnail.alt} src={thumbnail.src} />
</a>
{/each}
{#if $page.state.selected}
<Modal on:close={() => history.back()}>
<!-- pass page data to the +page.svelte component,
just like SvelteKit would on navigation -->
<PhotoPage data={$page.state.selected} />
</Modal>
{/if}
<script lang="ts">
import { preloadData, pushState, goto } from '$app/navigation';
import { page } from '$app/stores';
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
export let data;
</script>
{#each data.thumbnails as thumbnail}
<a
href="/photos/{thumbnail.id}"
on:click={async (e) => {
// bail if opening a new tab, or we're on too small a screen
if (e.metaKey || innerWidth < 640) return;
// prevent navigation
e.preventDefault();
const { href } = e.currentTarget;
// run `load` functions (or rather, get the result of the `load` functions
// that are already running because of `data-sveltekit-preload-data`)
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState(href, { selected: result.data });
} else {
// something bad happened! try navigating
goto(href);
}
}}
>
<img alt={thumbnail.alt} src={thumbnail.src} />
</a>
{/each}
{#if $page.state.selected}
<Modal on:close={() => history.back()}>
<!-- pass page data to the +page.svelte component,
just like SvelteKit would on navigation -->
<PhotoPage data={$page.state.selected} />
</Modal>
{/if}
注意事项永久链接
在服务器端渲染期间,$page.state
始终为空对象。用户首次登陆的页面也是如此——如果用户重新加载页面(或从另一个文档返回),则在他们导航之前,状态将不会被应用。
浅层路由是一项需要 JavaScript 才能工作的功能。在使用它时要小心,并尝试考虑在 JavaScript 不可用的情况下明智的回退行为。