核心概念
路由
在 GitHub 上编辑此页面SvelteKit 的核心是一个基于文件系统的路由器。应用程序的路由(即用户可以访问的 URL 路径)由代码库中的目录定义
src/routes
是根路由src/routes/about
创建一个/about
路由src/routes/blog/[slug]
创建一个带有参数slug
的路由,当用户请求诸如/blog/hello-world
之类的页面时,可以使用该参数动态加载数据
你可以通过编辑项目配置将
src/routes
更改为不同的目录。
每个路由目录包含一个或多个路由文件,可以通过其 +
前缀来识别。
+page永久链接
+page.svelte永久链接
+page.svelte
组件定义了应用程序的一个页面。默认情况下,页面在服务器上(SSR)渲染以进行初始请求,并在浏览器中(CSR)渲染以进行后续导航。
<h1>Hello and welcome to my site!</h1>
<a href="/about">About my site</a>
<h1>About this site</h1>
<p>TODO...</p>
<a href="/">Home</a>
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
请注意,SvelteKit 使用
<a>
元素在路由之间导航,而不是特定于框架的<Link>
组件。
+page.js永久链接
通常,页面需要在渲染之前加载一些数据。为此,我们添加了一个导出 load
函数的 +page.js
模块
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').PageLoad} */export functionload ({params }) {if (params .slug === 'hello-world') {return {title : 'Hello world!',content : 'Welcome to our blog. Lorem ipsum dolor sit amet...'};}error (404, 'Not found');}
ts
import {error } from '@sveltejs/kit';import type {PageLoad } from './$types';export constload :PageLoad = ({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.server.js永久链接
如果您的 load
函数只能在服务器上运行,例如,如果它需要从数据库中获取数据,或者您需要访问私有 环境变量,如 API 密钥,那么您可以将 +page.js
重命名为 +page.server.js
,并将 PageLoad
类型更改为 PageServerLoad
。
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {constpost = awaitgetPostFromDatabase (params .slug );if (post ) {returnpost ;}error (404, 'Not found');}
ts
import {error } from '@sveltejs/kit';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {constpost = awaitgetPostFromDatabase (params .slug );if (post ) {returnpost ;}error (404, 'Not found');};
在客户端导航期间,SvelteKit 将从服务器加载此数据,这意味着返回的值必须使用 devalue 进行序列化。有关 API 的完整详细信息,请参阅 load
。
与 +page.js
一样,+page.server.js
可以导出 页面选项 — prerender
、ssr
和 csr
。
+page.server.js
文件还可以导出操作。如果 load
允许您从服务器读取数据,则 actions
允许您使用 <form>
元素将数据写入服务器。要了解如何使用它们,请参阅 表单操作 部分。
+error永久链接
如果在 load
期间发生错误,SvelteKit 将呈现一个默认错误页面。您可以通过添加 +error.svelte
文件来按路由自定义此错误页面
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error.message}</h1>
<script lang="ts">
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error.message}</h1>
SvelteKit 会“向上查找”以寻找最近的错误边界——如果上面提到的文件不存在,它将尝试 src/routes/blog/+error.svelte
,然后尝试 src/routes/+error.svelte
,最后再渲染默认错误页面。如果该操作失败(或如果错误是由根 +layout
的 load
函数抛出的,该函数位于根 +error
的“上方”),SvelteKit 将退出并渲染一个静态回退错误页面,你可以通过创建一个 src/error.html
文件来对其进行自定义。
如果错误发生在 +layout(.server).js
中的 load
函数内部,树中最近的错误边界是该布局上方(而不是旁边)的 +error.svelte
文件。
如果找不到路由(404),将使用 src/routes/+error.svelte
(或如果该文件不存在,则使用默认错误页面)。
当错误发生在
handle
或 +server.js 请求处理程序内部时,不会使用+error.svelte
。
你可以在此处阅读有关错误处理的更多信息。
+layout永久链接
到目前为止,我们一直将页面视为完全独立的组件——在导航时,现有的 +page.svelte
组件将被销毁,一个新组件将取而代之。
但在许多应用中,有些元素应在每个页面上可见,例如顶级导航或页脚。与其在每个 +page.svelte
中重复它们,我们可以在布局中放置它们。
+layout.svelte永久链接
要创建一个适用于每个页面的布局,请创建一个名为 src/routes/+layout.svelte
的文件。默认布局(如果你不自己创建,SvelteKit 使用的布局)如下所示...
<slot></slot>
...但我们可以添加我们想要的任何标记、样式和行为。唯一的要求是组件包含页面内容的 <slot>
。例如,让我们添加一个导航栏
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/settings">Settings</a>
</nav>
<slot></slot>
如果我们为 /
、/about
和 /settings
创建页面...
<h1>Home</h1>
<h1>About</h1>
<h1>Settings</h1>
...导航将始终可见,并且在三个页面之间单击只会替换 <h1>
。
布局可以嵌套。假设我们不仅仅有一个 /settings
页面,而是有嵌套页面,例如 /settings/profile
和 /settings/notifications
,它们具有一个共享子菜单(有关真实示例,请参阅 github.com/settings)。
我们可以创建一个仅适用于 /settings
下方页面的布局(同时继承具有顶级导航的根布局)
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<h1>Settings</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
<slot></slot>
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<h1>Settings</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
<slot></slot>
您可以在下一节中查看 +layout.js
示例,了解如何填充 data
。
默认情况下,每个布局都会继承其上方的布局。有时这不是您想要的 - 在这种情况下,高级布局 可以帮助您。
+layout.js永久链接
就像 +page.svelte
从 +page.js
加载数据一样,您的 +layout.svelte
组件可以从 +layout.js
中的 load
函数获取数据。
ts
/** @type {import('./$types').LayoutLoad} */export functionload () {return {sections : [{slug : 'profile',title : 'Profile' },{slug : 'notifications',title : 'Notifications' }]};}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = () => {return {sections : [{slug : 'profile',title : 'Profile' },{slug : 'notifications',title : 'Notifications' },],};};
如果 +layout.js
导出 页面选项 — prerender
、ssr
和 csr
— 它们将用作子页面的默认值。
从布局的 load
函数返回的数据也对所有子页面可用
<script>
/** @type {import('./$types').PageData} */
export let data;
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
通常,在页面之间导航时布局数据不会改变。SvelteKit 将在必要时智能地重新运行
load
函数。
+layout.server.js永久链接
要在服务器上运行布局的 load
函数,请将其移动到 +layout.server.js
,并将 LayoutLoad
类型更改为 LayoutServerLoad
。
与 +layout.js
一样,+layout.server.js
可以导出 页面选项 — prerender
、ssr
和 csr
。
+server永久链接
除了页面之外,您还可以使用 +server.js
文件定义具有 GET
、POST
、PATCH
、PUT
、DELETE
、OPTIONS
和 HEAD
等 HTTP 动词的函数,这些函数采用 RequestEvent
参数并返回 Response
对象,来定义路由(有时称为“API 路由”或“端点”)。
例如,我们可以使用 GET
处理程序创建一个 /api/random-number
路由
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').RequestHandler} */export functionGET ({url }) {constmin =Number (url .searchParams .get ('min') ?? '0');constmax =Number (url .searchParams .get ('max') ?? '1');constd =max -min ;if (isNaN (d ) ||d < 0) {error (400, 'min and max must be numbers, and min must be less than max');}constrandom =min +Math .random () *d ;return newResponse (String (random ));}
ts
import {error } from '@sveltejs/kit';import type {RequestHandler } from './$types';export constGET :RequestHandler = ({url }) => {constmin =Number (url .searchParams .get ('min') ?? '0');constmax =Number (url .searchParams .get ('max') ?? '1');constd =max -min ;if (isNaN (d ) ||d < 0) {error (400, 'min and max must be numbers, and min must be less than max');}constrandom =min +Math .random () *d ;return newResponse (String (random ));};
Response
的第一个参数可以是 ReadableStream
,从而可以流式传输大量数据或创建服务器发送的事件(除非部署到会缓冲响应的平台,如 AWS Lambda)。
你可以使用 @sveltejs/kit
中的 error
、redirect
和 json
方法,以方便起见(但这不是必须的)。
如果抛出错误(error(...)
或意外错误),响应将是错误的 JSON 表示或备用错误页面——可通过 src/error.html
自定义——具体取决于 Accept
头。在这种情况下,+error.svelte
组件不会被渲染。你可以在此处阅读有关错误处理的更多信息。
在创建
OPTIONS
处理程序时,请注意 Vite 将注入Access-Control-Allow-Origin
和Access-Control-Allow-Methods
头——除非你添加它们,否则这些头在生产中不会存在。
接收数据永久链接
通过导出 POST
/PUT
/PATCH
/DELETE
/OPTIONS
/HEAD
处理程序,可以将 +server.js
文件用于创建完整的 API
<script>
let a = 0;
let b = 0;
let total = 0;
async function add() {
const response = await fetch('/api/add', {
method: 'POST',
body: JSON.stringify({ a, b }),
headers: {
'content-type': 'application/json'
}
});
total = await response.json();
}
</script>
<input type="number" bind:value={a}> +
<input type="number" bind:value={b}> =
{total}
<button on:click={add}>Calculate</button>
<script lang="ts">
let a = 0;
let b = 0;
let total = 0;
async function add() {
const response = await fetch('/api/add', {
method: 'POST',
body: JSON.stringify({ a, b }),
headers: {
'content-type': 'application/json',
},
});
total = await response.json();
}
</script>
<input type="number" bind:value={a}> +
<input type="number" bind:value={b}> =
{total}
<button on:click={add}>Calculate</button>
ts
import {json } from '@sveltejs/kit';/** @type {import('./$types').RequestHandler} */export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}
ts
import {json } from '@sveltejs/kit';import type {RequestHandler } from './$types';export constPOST :RequestHandler = async ({request }) => {const {a ,b } = awaitrequest .json ();returnjson (a +b );};
通常情况下,表单操作是将数据从浏览器提交到服务器的更好方法。
如果导出了
GET
处理程序,则HEAD
请求将返回GET
处理程序响应正文的content-length
。
备用方法处理程序永久链接
导出 fallback
处理程序将匹配任何未处理的请求方法,包括 MOVE
等没有从 +server.js
专用导出的方法。
ts
import {json ,text } from '@sveltejs/kit';export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}// This handler will respond to PUT, PATCH, DELETE, etc./** @type {import('./$types').RequestHandler} */export async functionfallback ({request }) {returntext (`I caught your ${request .method } request!`);}
ts
import {json ,text } from '@sveltejs/kit';import type {RequestHandler } from './$types';export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}// This handler will respond to PUT, PATCH, DELETE, etc.export constfallback :RequestHandler = async ({request }) => {returntext (`I caught your ${request .method } request!`);};
对于
HEAD
请求,GET
处理程序优先于fallback
处理程序。
内容协商永久链接
+server.js
文件可以放置在与 +page
文件相同的目录中,从而允许同一路由成为页面或 API 端点。为了确定哪一个,SvelteKit 应用以下规则
PUT
/PATCH
/DELETE
/OPTIONS
请求始终由+server.js
处理,因为它们不适用于页面- 如果
accept
头部优先考虑text/html
(换句话说,这是一个浏览器页面请求),则GET
/POST
/HEAD
请求将被视为页面请求,否则将由+server.js
处理。 - 对
GET
请求的响应将包含一个Vary: Accept
头部,以便代理和浏览器分别缓存 HTML 和 JSON 响应。
$types永久链接
在上述所有示例中,我们一直在从 $types.d.ts
文件导入类型。如果你使用 TypeScript(或带有 JSDoc 类型注释的 JavaScript)来在处理根文件时提供类型安全性,SvelteKit 会在隐藏目录中为你创建一个这样的文件。
例如,使用 PageData
(或 LayoutData
,用于 +layout.svelte
文件)注释 export let data
会告诉 TypeScript,data
的类型是 load
返回的任何内容
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
反过来,使用 PageLoad
、PageServerLoad
、LayoutLoad
或 LayoutServerLoad
(分别用于 +page.js
、+page.server.js
、+layout.js
和 +layout.server.js
)注释 load
函数可确保 params
和返回值的类型正确。
如果你使用的是 VS Code 或任何支持语言服务器协议和 TypeScript 插件的 IDE,那么你可以完全省略这些类型!Svelte 的 IDE 工具会为你插入正确的类型,这样你就可以在不自己编写类型的情况下获得类型检查。它还可以与我们的命令行工具 svelte-check
一起使用。
你可以在我们的 博客文章 中阅读有关省略 $types
的更多信息。
其他文件永久链接
SvelteKit 会忽略路由目录中的任何其他文件。这意味着你可以将组件和实用程序模块与需要它们的路由放在一起。
如果多个路由需要组件和模块,最好将它们放在 $lib
中。