Skip to main content

浅路由

当你在 SvelteKit 应用中导航时,你会创建历史记录条目。单击后退和前进按钮会遍历此条目列表,重新运行任何 load 函数并根据需要替换页面组件。

¥As you navigate around a SvelteKit app, you create history entries. Clicking the back and forward buttons traverses through this list of entries, re-running any load functions and replacing page components as necessary.

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

¥Sometimes, it’s useful to create history entries without navigating. For example, you might want to show a modal dialog that the user can dismiss by navigating back. This is particularly valuable on mobile devices, where swipe gestures are often more natural than interacting directly with the UI. In these cases, a modal that is not associated with a history entry can be a source of frustration, as a user may swipe backwards in an attempt to dismiss it and find themselves on the wrong page.

SvelteKit 通过 pushStatereplaceState 函数实现了这一点,这些函数允许你将状态与历史记录条目关联而无需导航。例如,要实现历史驱动的模式:

¥SvelteKit makes this possible with the pushState and replaceState functions, which allow you to associate state with a history entry without navigating. For example, to implement a history-driven modal:

+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 回调运行的方式与其交互来关闭模式,这将以编程方式向后导航。

¥The modal can be dismissed by navigating back (unsetting page.state.showModal) or by interacting with it in a way that causes the close callback to run, which will navigate back programmatically.

API

pushState 的第一个参数是 URL,相对于当前 URL。要保留当前 URL,请使用 ''

¥The first argument to pushState is the URL, relative to the current URL. To stay on the current URL, use ''.

第二个参数是新的页面状态,可以通过 页面对象 作为 page.state 访问。你可以通过声明 App.PageState 接口(通常在 src/app.d.ts 中)使页面状态类型安全。

¥The second argument is the new page state, which can be accessed via the page object as page.state. You can make page state type-safe by declaring an App.PageState interface (usually in src/app.d.ts).

要在不创建新历史记录条目的情况下设置页面状态,请使用 replaceState 而不是 pushState

¥To set page state without creating a new history entry, use replaceState instead of pushState.

Legacy mode

SvelteKit 2.12 中添加了来自 $app/statepage.state。如果你使用的是早期版本或使用 Svelte 4,请改用 $app/stores 中的 $page.state

¥[!LEGACY] page.state from $app/state was added in SvelteKit 2.12. If you’re using an earlier version or are using Svelte 4, use $page.state from $app/stores instead.

为路由加载数据(Loading data for a route)

¥Loading data for a route

当浅路由时,你可能希望在当前页面内渲染另一个 +page.svelte。例如,单击照片缩略图可以弹出详细信息视图而无需导航到照片页面。

¥When shallow routing, you may want to render another +page.svelte inside the current page. For example, clicking on a photo thumbnail could pop up the detail view without navigating to the photo page.

为了使其工作,你需要加载 +page.svelte 期望的数据。一种方便的方法是在 <a> 元素的 click 处理程序内使用 preloadData。如果元素(或父元素)使用 data-sveltekit-preload-data,则数据已被请求,preloadData 将重用该请求。

¥For this to work, you need to load the data that the +page.svelte expects. A convenient way to do this is to use preloadData inside the click handler of an <a> element. If the element (or a parent) uses data-sveltekit-preload-data, the data will have already been requested, and preloadData will reuse that request.

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}

注意事项(Caveats)

¥Caveats

在服务器端渲染过程中,page.state 始终是一个空对象。用户进入的第一个页面也是如此 - 如果用户重新加载页面(或从另一个文档返回),则在他们导航之前不会应用状态。

¥During server-side rendering, page.state is always an empty object. The same is true for the first page the user lands on — if the user reloads the page (or returns from another document), state will not be applied until they navigate.

浅路由是一项需要 JavaScript 才能运行的功能。使用时要小心,并尝试在 JavaScript 不可用的情况下考虑合理的后备行为。

¥Shallow routing is a feature that requires JavaScript to work. Be mindful when using it and try to think of sensible fallback behavior in case JavaScript isn’t available.

上一页 下一页