SvelteKit 的核心是一个基于文件系统的路由器*。您的应用程序的路由——即用户可以访问的 URL 路径——由代码库中的目录定义:* * `src/routes` 是根路由 * `src/routes/about` 创建了一个 `/about` 路由 * `src/routes/blog/[slug]` 创建了一个带有 *参数* 的路由,`slug`,可以在用户请求类似 `/blog/hello-world` 的页面时动态加载数据 > \[!注意\] 您可以通过编辑 [项目配置](configuration) 将 `src/routes` 改为不同的目录。 每个路由目录包含一个或多个*路由文件*,可以通过它们的`+`前缀来识别。 我们将稍后更详细地介绍这些文件,但这里有一些简单的规则可以帮助您记住 SvelteKit 的路由工作方式: * 所有文件都可以在服务器上运行 * 所有文件都在客户端运行,除了`+服务器`文件 * `+layout` 和 `+error` 文件同样适用于子目录以及它们所在的目录 ## +页面 ### +page.svelte 一个 `+page.svelte` 组件定义了您的应用页面。默认情况下,页面在服务器([SSR](glossary#SSR))上对初始请求进行渲染,并在浏览器([CSR](glossary#CSR))上对后续导航进行渲染。 ```svelte

Hello and welcome to my site!

About my site ``` ```svelte

About this site

TODO...

Home ``` > \[注意\] SvelteKit 使用``元素在路由之间导航,而不是框架特定的``组件。 页面可以通过`load`函数通过`data`属性接收数据。 ```svelte

{data.title}

{@html data.content}
``` > \[!旧版\] `PageProps` 在 2.16.0 版本中添加。在早期版本中,您必须手动输入 `data` 属性,使用 `PageData` 代替,参见 \[类型\](#\\types)。 > > 在 Svelte 4 中,您将使用 `export let data` 代替。 ### +page.js 通常,页面在渲染之前需要加载一些数据。为此,我们添加了一个 `+page.js` 模块,该模块导出了一个 `load` 函数: ```js /// file: src/routes/blog/[slug]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load({ params }) { if (params.slug === 'hello-world') { return { title: 'Hello world!', content: 'Welcome to our blog. Lorem ipsum dolor sit amet...' }; } error(404, 'Not found'); } ``` 此函数与`+page.svelte`并行运行,这意味着它在服务器端渲染期间在服务器上运行,在客户端导航期间在浏览器中运行。有关 API 的完整详细信息,请参阅`load`。 除了`load`,`+page.js`还可以导出配置页面行为的值: * `export const prerender = true` 或 `false` 或 `'auto'` * `export const ssr = true` 或 `false` * `export const csr = true` 或 `false` 您可以在[页面选项](page-options)中找到更多关于这些信息。 ### +page.server.js 如果您的`load`函数只能在服务器上运行——例如,如果它需要从数据库获取数据或您需要访问像 API 密钥这样的私有[环境变量]($env-static-private)——那么您可以重命名`+page.js`为`+page.server.js`并将`PageLoad`类型更改为`PageServerLoad`。 ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare global { const getPostFromDatabase: (slug: string) => { title: string; content: string; } } export {}; // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await getPostFromDatabase(params.slug); if (post) { return post; } error(404, 'Not found'); } ``` 在客户端导航期间,SvelteKit 将从服务器加载此数据,这意味着返回的值必须使用[devalue](https://github.com/rich-harris/devalue)进行序列化。请参阅[`load`](load)以获取 API 的完整详细信息。 与`+page.js`类似,`+page.server.js`可以导出[页面选项](page-options)——`预渲染`、`服务器端渲染`和`客户端渲染`。 一个 `+page.server.js` 文件也可以导出 *actions*。如果 `load` 允许您从服务器读取数据,`actions` 则允许您使用 `
` 元素将数据 *写入* 服务器。要了解如何使用它们,请参阅 [表单操作](form-actions) 部分。 ## +错误 如果加载过程中发生错误,SvelteKit 将渲染默认错误页面。您可以通过添加一个`+error.svelte`文件来根据每个路由自定义此错误页面: ```svelte

{page.status}: {page.error.message}

``` > \[!旧版\] `$app/state` 在 SvelteKit 2.12 中被添加。如果您使用的是更早的版本或使用 Svelte 4,请使用 `$app/stores` 代替。 SvelteKit 将“遍历树形结构”以寻找最近的错误边界——如果上面的文件不存在,它将尝试`src/routes/blog/+error.svelte`,然后是`src/routes/+error.svelte`,在渲染默认错误页面之前。如果*这*失败了(或者如果错误是从根`load`函数中抛出的,该函数位于根`+layout`之上,而`+error`位于其“上方”),SvelteKit 将退出并渲染一个静态的回退错误页面,您可以通过创建一个`src/error.html`文件来自定义它。 如果错误发生在 `load` 函数内部,在 `+layout(.server).js` 中,树中最接近的错误边界是一个位于布局 *上方* 的 `+error.svelte` 文件(而不是紧挨着它)。 如果找不到路由(404),将使用`src/routes/+error.svelte`(或默认错误页面,如果该文件不存在)。 > \[!注意\] `+error.svelte` 在 [`handle`](hooks#Server-hooks-handle) 或 [+server.js](#server) 请求处理器内部发生错误时 *不* 被使用。 您可以在[这里](errors)了解更多关于错误处理的信息。 ## +布局 截至目前,我们将页面视为完全独立的组件——在导航时,现有的`+page.svelte`组件将被销毁,并由新的一个取代。 但许多应用中,有一些元素应该在每一页都可见,例如顶级导航或页脚。我们不必在每一页的`+page.svelte`中重复它们,而是可以将它们放在*布局*中。 ### +layout.svelte 为创建适用于所有页面的布局,创建一个名为 `src/routes/+layout.svelte` 的文件。默认布局(SvelteKit 在没有提供自己的布局时使用的布局)看起来像这样... ```svelte {@render children()} ``` ...但我们可以添加任何标记、样式和行为。唯一的要求是组件必须包含一个用于页面内容的`@render`标签。例如,让我们添加一个导航栏: ```svelte
{@render children()} ``` 如果我们为`/`、`/about`和`/settings`创建页面... ```html /// file: src/routes/+page.svelte

Home

``` ```html /// file: src/routes/about/+page.svelte

About

``` ```html /// file: src/routes/settings/+page.svelte

Settings

``` ...导航将始终可见,在三个页面之间点击只会导致 `

` 被替换。 布局可以是嵌套的。假设我们不仅仅有一个单独的 `/settings` 页面,而是有嵌套页面,如 `/settings/profile` 和 `/settings/notifications`,它们共享一个子菜单(以现实生活中的例子,见 [github.com/settings](https://github.com/settings))。 我们可以创建一个仅适用于页面下方 `/settings` 的布局(同时继承顶级导航的根布局): ```svelte

Settings

{@render children()} ``` > \[!旧版\] `LayoutProps` 在 2.16.0 版本中添加。在早期版本中,您必须 [手动输入属性](#$types)。 您可以通过查看下一节中位于下面的`+layout.js`示例来了解`数据`是如何填充的。 默认情况下,每个布局都继承其上方的布局。有时这并不是你想要的 - 在这种情况下,[高级布局](advanced-routing#Advanced-layouts)可以帮助你。 ### +layout.js 就像 `+page.svelte` 从 `+page.js` 加载数据一样,你的 `+layout.svelte` 组件可以从 `+layout.js` 中的 [`load`](load) 函数获取数据。 ```js /// file: src/routes/settings/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { sections: [ { slug: 'profile', title: 'Profile' }, { slug: 'notifications', title: 'Notifications' } ] }; } ``` 如果 `+layout.js` 导出 [页面选项](page-options) —— `预渲染`、`SSR` 和 `CSR` —— 它们将被用作子页面的默认值。 数据从布局的 `加载` 函数返回,也对其所有子页面可用: ```svelte ``` > \[注意\] 在页面间导航时,布局数据通常保持不变。SvelteKit 将在必要时智能地重新运行[`加载`](load)函数。 ### +layout.server.js 要在服务器上运行您布局的`load`函数,请将其移动到`+layout.server.js`,并将`LayoutLoad`类型更改为`LayoutServerLoad`。 与`+layout.js`类似,`+layout.server.js`可以导出[页面选项](page-options)——`预渲染`、`服务器端渲染`和`客户端渲染`。 ## +服务器 以及页面,您还可以使用`+server.js`文件(有时称为“API 路由”或“端点”)来定义路由,该文件让您完全控制响应。您的`+server.js`文件导出与 HTTP 动词(如`GET`、`POST`、`PATCH`、`PUT`、`DELETE`、`OPTIONS`和`HEAD`)对应的函数,这些函数接受一个[`RequestEvent`](@sveltejs-kit#RequestEvent)参数并返回一个[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)对象。 例如,我们可以创建一个 `/api/random-number` 路由,并使用一个 `GET` 处理器: ```js /// file: src/routes/api/random-number/+server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export function GET({ url }) { const min = Number(url.searchParams.get('min') ?? '0'); const max = Number(url.searchParams.get('max') ?? '1'); const d = max - min; if (isNaN(d) || d < 0) { error(400, 'min and max must be numbers, and min must be less than max'); } const random = min + Math.random() * d; return new Response(String(random)); } ``` 第一个参数可以是[`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream),这使得可以流式传输大量数据或创建服务器发送事件(除非部署到像 AWS Lambda 这样的缓冲响应的平台)。 您可以使用来自 `@sveltejs/kit` 的 [`error`](@sveltejs-kit#error)、[`redirect`](@sveltejs-kit#redirect) 和 [`json`](@sveltejs-kit#json) 方法以提高便利性(但您不必这样做)。 如果抛出错误(无论是 `error(...)` 还是意外错误),响应将是一个错误或回退错误页面的 JSON 表示形式——可以通过 `src/error.html` 进行自定义——具体取决于 `Accept` 头部。在这种情况下,[`+error.svelte`](#error) 组件将 *不会* 被渲染。您可以在 [这里](errors) 了解更多关于错误处理的信息。 > \[!注意\] 当创建一个 `OPTIONS` 处理器时,请注意 Vite 将注入 `Access-Control-Allow-Origin` 和 `Access-Control-Allow-Methods` 头部信息——除非您添加它们,否则在生产环境中这些信息将不存在。 > \[!注意\] `+layout` 文件对 `+server.js` 文件没有影响。如果您想在每次请求之前运行一些逻辑,请将其添加到服务器的 [`handle`](hooks#Server-hooks-handle) 钩子中。 ### 接收数据 通过导出 `POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`/`HEAD` 处理程序,可以使用 `+server.js` 文件创建完整的 API: ```svelte + = {total} ``` ```js /// file: src/routes/api/add/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } ``` > \[!注意\] 通常,[表单操作](form-actions)是从浏览器向服务器提交数据的一种更好的方式。 > \[注意\] 如果导出 `GET` 处理器,则 `HEAD` 请求将返回 `content-length`,这是 `GET` 处理器响应体的长度。 ### 回退方法处理器 导出 `fallback` 处理器将匹配任何未处理的请求方法,包括像 `MOVE` 这样没有从 `+server.js` 中专门导出的方法。 ```js /// file: src/routes/api/add/+server.js import { json, text } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } // This handler will respond to PUT, PATCH, DELETE, etc. /** @type {import('./$types').RequestHandler} */ export async function fallback({ request }) { return text(`I caught your ${request.method} request!`); } ``` > \[!注意\] 对于 `HEAD` 请求,`GET` 处理器优先于 `fallback` 处理器。 ### 内容协商 `+server.js` 文件可以放置在与 `+page` 文件相同的目录中,允许相同的路由既是页面也是 API 端点。为了确定是哪一个,SvelteKit 应用以下规则: * `PUT`/`PATCH`/`DELETE`/`OPTIONS` 请求始终由 `+server.js` 处理,因为它们不适用于页面 * `GET`/`POST`/`HEAD` 请求,如果 `accept` 头部优先级为 `text/html`(换句话说,它是一个浏览器页面请求),则被视为页面请求;否则,由 `+server.js` 处理。 * 响应 `GET` 请求将包含一个 `Vary: Accept` 标头,以便代理和浏览器分别缓存 HTML 和 JSON 响应。 ## $types 在整个上述示例中,我们一直在从`$types.d.ts`文件中导入类型。这是一个 SvelteKit 为您在隐藏目录中创建的文件,如果您使用 TypeScript(或带有 JSDoc 类型注解的 JavaScript)进行开发,它会在处理根文件时为您提供类型安全。 例如,将 `let { data } = $props()` 注释为 `PageProps`(或 `LayoutProps`,对于 `+layout.svelte` 文件)告诉 TypeScript,`data` 的类型是 `load` 返回的内容。 ```svelte ``` > \[注意\] 在 2.16.0 版本中添加的 `PageProps` 和 `LayoutProps` 类型,是输入 `data` 属性为 `PageData` 或 `LayoutData` 的快捷方式,以及其他属性,例如页面的 `form` 或布局的 `children`。在早期版本中,您必须手动输入这些属性。例如,对于一个页面: > > ```js > /// file: +page.svelte > /** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */ > let { data, form } = $props(); > ``` > > 或者,对于布局: > > ```js > /// file: +layout.svelte > /** @type {{ data: import('./$types').LayoutData, children: Snippet }} */ > let { data, children } = $props(); > ``` 依次注释 `load` 函数为 `PageLoad`、`PageServerLoad`、`LayoutLoad` 或 `LayoutServerLoad`(分别对应于 `+page.js`、`+page.server.js`、`+layout.js` 和 `+layout.server.js`),确保 `params` 和返回值类型正确。 如果您正在使用 VS Code 或支持语言服务器协议和 TypeScript 插件的任何 IDE,那么您可以完全省略这些类型!Svelte 的 IDE 工具将为您插入正确的类型,因此您将获得类型检查而无需自己编写。它还与我们的命令行工具 `svelte-check` 一起工作。 您可以在我们的[博客文章](/blog/zero-config-type-safety)中了解更多关于省略`$types`的信息。 ## 其他文件 任何位于路由目录内的其他文件都将被 SvelteKit 忽略。这意味着您可以将组件和需要它们的实用模块与路由一起放置。 如果组件和模块被多个路由需要,将它们放在[`$lib`]($lib)中是个好主意。 ## 进一步阅读 * [ 教程:路由](/tutorial/kit/pages) * [ 教程:API 路由](/tutorial/kit/get-handlers) * [ 文档:高级路由](advanced-routing)