浅层路由
当您在 SvelteKit 应用中导航时,您会创建历史记录条目。点击后退和前进按钮会遍历这个条目列表,重新运行所有 load
函数,并在必要时替换页面组件。
有时,在不导航的情况下创建历史条目是有用的。例如,您可能想要显示一个模态对话框,用户可以通过返回导航来关闭它。这在移动设备上特别有价值,因为滑动手势通常比直接与 UI 交互更自然。在这些情况下,没有关联历史记录条目的模态可能会令人沮丧,因为用户可能会尝试向后滑动来关闭它,却发现自己到了错误的页面。
SvelteKit 通过 pushState
和 replaceState
函数使这成为可能,这些函数允许您在不进行导航的情况下将状态与历史记录条目关联。例如,要实现一个由历史驱动的模态:
<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}
<script lang="ts">
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
回调运行来关闭。
API
pushState
的第一个参数是相对于当前 URL 的 URL。要保持在当前 URL,使用 ''
。
第二个参数是新的页面状态,可以通过 page 对象 作为 page.state
访问。您可以通过声明 App.PageState
接口(通常在 src/app.d.ts
中)来使页面状态类型安全。
要设置页面状态而不创建新的历史记录条目,请使用 replaceState
而不是 pushState
。
[!旧版说明] >
$app/state
中的page.state
是在 SvelteKit 2.12 中添加的。如果您使用的是较早版本或正在使用 Svelte 4,请使用$app/stores
中的$page.state
。
为路由加载数据
在进行浅层路由时,您可能想在当前页面内渲染另一个 +page.svelte
。例如,点击照片缩略图可以弹出详细视图,而不需要导航到照片页面。
为此,您需要加载 +page.svelte
所需的数据。一个便捷的方法是在 <a>
元素的 click
处理程序中使用 preloadData
。如果元素(或其父元素)使用 data-sveltekit-preload-data
,数据将已经被请求,preloadData
将复用该请求。
<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` 函数(或者说,获取由于 `data-sveltekit-preload-data`
// 而已经在运行的 `load` 函数的结果)
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}
<script lang="ts">
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` 函数(或者说,获取由于 `data-sveltekit-preload-data`
// 而已经在运行的 `load` 函数的结果)
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}
注意事项
在服务端渲染期间,page.state
始终是一个空对象。对于用户首次访问的页面也是如此 — 如果用户重新加载页面(或从另一个文档返回),状态将不会应用,直到他们进行导航。
浅层路由是一个需要 JavaScript 才能工作的功能。在使用它时要谨慎,并尝试考虑在 JavaScript 不可用时的合理后备行为。