Skip to main content

Shallow routing

当你浏览 SvelteKit 应用时,你会创建历史记录条目。点击后退和前进按钮会遍历此条目列表,重新运行任何 加载 函数,并在必要时替换页面组件。

有时,在不导航的情况下创建历史记录条目很有用。无需导航。例如,您可能希望显示一个用户可以通过导航返回来关闭的模态对话框。这在移动设备上尤其有价值,因为在移动设备上,滑动手势通常比直接与 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

第一个参数是相对于当前 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 将重用该请求。

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        // 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 不可用时的合理回退行为。

Edit this page on GitHub llms.txt

previous next