Skip to main content

浅层路由

当您在 SvelteKit 应用中导航时,您会创建历史记录条目。点击后退和前进按钮会遍历这个条目列表,重新运行所有 load 函数,并在必要时替换页面组件。

有时,在不导航的情况下创建历史条目是有用的。例如,您可能想要显示一个模态对话框,用户可以通过返回导航来关闭它。这在移动设备上特别有价值,因为滑动手势通常比直接与 UI 交互更自然。在这些情况下,没有关联历史记录条目的模态可能会令人沮丧,因为用户可能会尝试向后滑动来关闭它,却发现自己到了错误的页面。

SvelteKit 通过 pushStatereplaceState 函数使这成为可能,这些函数允许您在不进行导航的情况下将状态与历史记录条目关联。例如,要实现一个由历史驱动的模态:

+page
<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 将复用该请求。

src/routes/photos/+page
<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 不可用时的合理后备行为。

在 GitHub 编辑此页面

上一页 下一页