核心概念
路由
在 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 模块
tsimport {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');}
tsimport {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或falseexport const csr = true或false
您可以在 页面选项 中找到有关这些选项的更多信息。
+page.server.js永久链接
如果您的 load 函数只能在服务器上运行,例如,如果它需要从数据库中获取数据,或者您需要访问私有 环境变量,如 API 密钥,那么您可以将 +page.js 重命名为 +page.server.js,并将 PageLoad 类型更改为 PageServerLoad。
tsimport {error } from '@sveltejs/kit';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {constpost = awaitgetPostFromDatabase (params .slug );if (post ) {returnpost ;}error (404, 'Not found');}
tsimport {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' }]};}
tsimport 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 路由
tsimport {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 ));}
tsimport {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>tsimport {json } from '@sveltejs/kit';/** @type {import('./$types').RequestHandler} */export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}
tsimport {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 专用导出的方法。
tsimport {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!`);}
tsimport {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 中。