Shallow routing
当你浏览 SvelteKit 应用时,你会创建历史记录条目。点击后退和前进按钮会遍历此条目列表,重新运行任何 加载 函数,并在必要时替换页面组件。
有时,在不导航的情况下创建历史记录条目很有用。无需导航。例如,您可能希望显示一个用户可以通过导航返回来关闭的模态对话框。这在移动设备上尤其有价值,因为在移动设备上,滑动手势通常比直接与 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
第一个参数是相对于当前 URL 的 URL,要保持在当前 URL,请使用''。
第二个参数是新的页面状态,可以通过页面对象作为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 // bail if the screen is too small
|| e.shiftKey // or the link is opened in a new window
|| e.metaKey || e.ctrlKey // or a new tab (mac: metaKey, win/linux: ctrlKey)
// should also consider clicking with a mouse scroll wheel
) 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 onclose={() => 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/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 // bail if the screen is too small
|| e.shiftKey // or the link is opened in a new window
|| e.metaKey || e.ctrlKey // or a new tab (mac: metaKey, win/linux: ctrlKey)
// should also consider clicking with a mouse scroll wheel
) 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 onclose={() => 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 不可用时的合理回退行为。