Skip to main content

表单 actions

+page.server.js 文件可以导出 actions,允许您使用 <form> 元素向服务端 POST 数据。

使用 <form> 时,客户端 JavaScript 是可选的,但您可以轻松地使用 JavaScript 渐进式增强 表单交互,以提供最佳的用户体验。

默认 action

在最简单的情况下,一个页面声明一个 default action:

src/routes/login/+page.server
/** @satisfies {import('./$types').Actions} */
export const 
const actions: {
    default: (event: any) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
default: (event: any) => Promise<void>default: async (event: anyevent) => { // TODO log the user in } };
import type { 
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    default: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
actions
= {
default: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>default: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO log the user in } } satisfies
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

要从 /login 页面调用此 action,只需添加一个 <form> —— 不需要 JavaScript:

src/routes/login/+page
<form method="POST">
  <label>
	Email
	<input name="email" type="email">
  </label>
  <label>
	Password
	<input name="password" type="password">
  </label>
  <button>Log in</button>
</form>

如果有人点击按钮,浏览器将通过 POST 请求将表单数据发送到服务端,运行默认 action。

action 总是使用 POST 请求,因为 GET 请求不应该有副作用。

我们还可以通过添加 action 属性,调用来自其他页面的 action (例如,如果根布局中的导航栏有一个登录小部件):

src/routes/+layout
<form method="POST" action="/login">
	<!-- content -->
</form>

命名 actions

页面可以根据需要拥有多个命名 action ,而不是只有一个 default action:

src/routes/login/+page.server
/** @satisfies {import('./$types').Actions} */
export const 
const actions: {
    login: (event: any) => Promise<void>;
    register: (event: any) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
default: async (event) => { login: (event: any) => Promise<void>login: async (event: anyevent) => { // TODO log the user in }, register: (event: any) => Promise<void>register: async (event: anyevent) => { // TODO register the user } };
import type { 
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>;
    register: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
actions
= {
default: async (event) => { login: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>login: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO log the user in }, register: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } } satisfies
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

要调用命名 action ,添加一个以 / 字符为前缀的查询参数:

src/routes/login/+page
<form method="POST" action="?/register">
src/routes/+layout
<form method="POST" action="/login?/register">

除了 action 属性,我们还可以在按钮上使用 formaction 属性,将相同的表单数据 POST 到与父 <form> 不同的 action :

src/routes/login/+page
<form method="POST" action="?/login">
  <label>
	Email
	<input name="email" type="email">
  </label>
  <label>
	Password
	<input name="password" type="password">
  </label>
  <button>Log in</button>
  <button formaction="?/register">Register</button>
</form>

我们不能在命名 action 旁边有默认 action ,因为如果您在没有重定向的情况下 POST 到命名 action ,查询参数会保留在 URL 中,这意味着下一个默认 POST 将通过之前的命名 action 进行处理。

action 的结构

每个 action 接收一个 RequestEvent 对象,允许您使用 request.formData() 读取数据。在处理请求之后(例如,通过设置 cookie 让用户登录),action 可以响应数据,这些数据将在对应页面的 form 属性以及整个应用范围的 page.form 中可用,直到下一次更新。

src/routes/login/+page.server
import * as module "$lib/server/db"db from '$lib/server/db';

/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageServerLoad}
load
({ cookies: Cookies

Get or set cookies related to the current request

cookies
}) {
const const user: anyuser = await module "$lib/server/db"db.getUserFromSession(cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return { user: anyuser }; } /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
        success: boolean;
    }>;
    register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
    success: boolean;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } };
import * as module "$lib/server/db"db from '$lib/server/db';
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad, 
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const const load: PageServerLoadload: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad = async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
}) => {
const const user: anyuser = await module "$lib/server/db"db.getUserFromSession(cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return { user: anyuser }; }; export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
        success: boolean;
    }>;
    register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
    success: boolean;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;
src/routes/login/+page
<script>
  /** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */
  let { data, form } = $props();
</script>

{#if form?.success}
  <!-- 这个消息是短暂的;它存在是因为页面是响应表单提交而渲染的。如果用户重新加载,消息将消失 -->
  <p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}
<script lang="ts">
	import type { PageData, ActionData } from './$types';

  let { data, form }: { data: PageData, form: ActionData } = $props();
</script>

{#if form?.success}
  <!-- 这个消息是短暂的;它存在是因为页面是响应表单提交而渲染的。如果用户重新加载,消息将消失 -->
  <p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}
Legacy mode

在 Svelte 4 中,您将使用 export let dataexport let form 来声明属性

验证错误

如果请求因数据无效而无法处理,您可以将验证错误 —— 以及之前提交的表单值 —— 返回给用户,以便他们可以重试。fail 函数允许您返回一个 HTTP 状态码(通常是 400 或 422,用于验证错误)以及数据。状态码可以通过 page.status 获取,数据可以通过 form 获取:

src/routes/login/+page.server
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: string | null;
        missing: boolean;
    }> | ActionFailure<{
        ...;
    }> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: string | null;
    missing: boolean;
}> | ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); if (!const email: FormDataEntryValue | nullemail) { return
fail<{
    email: string | null;
    missing: boolean;
}>(status: number, data: {
    email: string | null;
    missing: boolean;
}): ActionFailure<{
    email: string | null;
    missing: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: string | nullemail, missing: booleanmissing: true });
} const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValueemail); if (!const user: anyuser || const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue;
    incorrect: boolean;
}): ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValueemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } };
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; import type {
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: string | null;
        missing: boolean;
    }> | ActionFailure<{
        ...;
    }> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: string | null;
    missing: boolean;
}> | ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); if (!const email: FormDataEntryValue | nullemail) { return
fail<{
    email: string | null;
    missing: boolean;
}>(status: number, data: {
    email: string | null;
    missing: boolean;
}): ActionFailure<{
    email: string | null;
    missing: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: string | nullemail, missing: booleanmissing: true });
} const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValueemail); if (!const user: anyuser || const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue;
    incorrect: boolean;
}): ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValueemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

请注意,作为预防措施,我们只将电子邮件返回给页面 —— 而不是密码。

src/routes/login/+page
<form method="POST" action="?/login">
	{#if form?.missing}<p class="error">邮箱字段为必填项</p>{/if}
  {#if form?.incorrect}<p class="error">凭据无效!</p>{/if}
  <label>
	Email
	<input name="email" type="email" value={form?.email ?? ''}>
  </label>
  <label>
	Password
	<input name="password" type="password">
  </label>
  <button>Log in</button>
  <button formaction="?/register">Register</button>
</form>

返回的数据必须可序列化为 JSON。除此之外,结构完全由您决定。例如,如果页面上有多个表单,您可以使用 id 属性或类似的方式区分返回的 form 数据对应哪个 <form>

重定向

重定向(和错误)与 load 中的工作方式完全相同:

src/routes/login/+page.server
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: FormDataEntryValue | null;
        missing: boolean;
    }> | ActionFailure<...> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: FormDataEntryValue | null;
    missing: boolean;
}> | ActionFailure<...> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
, url: URL

The requested URL.

url
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); if (!const user: anyuser) { return
fail<{
    email: FormDataEntryValue | null;
    missing: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    missing: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, missing: booleanmissing: true });
} if (const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue | null;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    incorrect: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
if (url: URL

The requested URL.

url
.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.has(name: string, value?: string): boolean

Returns a Boolean indicating if such a search parameter exists.

MDN Reference

has
('redirectTo')) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, url: URL

The requested URL.

url
.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.get(name: string): string | null

Returns the first value associated to the given search parameter.

MDN Reference

get
('redirectTo'));
} return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } };
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; import type {
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: FormDataEntryValue | null;
        missing: boolean;
    }> | ActionFailure<...> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
actions
= {
login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: FormDataEntryValue | null;
    missing: boolean;
}> | ActionFailure<...> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object

request
, url: URL

The requested URL.

url
}) => {
const const data: FormDatadata = await request: Request

The original request object

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); if (!const user: anyuser) { return
fail<{
    email: FormDataEntryValue | null;
    missing: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    missing: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, missing: booleanmissing: true });
} if (const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue | null;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    incorrect: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set(name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
if (url: URL

The requested URL.

url
.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.has(name: string, value?: string): boolean

Returns a Boolean indicating if such a search parameter exists.

MDN Reference

has
('redirectTo')) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, url.searchParams.get('redirectTo'));
} return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO register the user } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

加载数据

action 运行后,页面将重新渲染(除非发生重定向或意外错误), action 的返回值将作为 form 属性提供给页面。这意味着页面的 load 函数将在 action 完成后运行。

请注意,handle 在 action 被调用之前运行,并且不会在 load 函数之前重新运行。这意味着,例如,如果您使用 handle 根据 cookie 填充 event.locals,则在 action 中设置或删除 cookie 时,必须更新 event.locals

src/hooks.server
/** @type {import('@sveltejs/kit').Handle} */
export async function 
function handle(input: {
    event: RequestEvent;
    resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}): MaybePromise<...>
@type{import('@sveltejs/kit').Handle}
handle
({ event: RequestEvent<Partial<Record<string, string>>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) {
event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= await
function getUser(sessionid: string | undefined): {
    name: string;
}
getUser
(event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Partial<Record<string, string>>, string | null>event); }
import type { 
type Handle = (input: {
    event: RequestEvent;
    resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}) => MaybePromise<...>

The handle hook runs every time the SvelteKit server receives a request and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).

Handle
} from '@sveltejs/kit';
export const const handle: Handlehandle:
type Handle = (input: {
    event: RequestEvent;
    resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}) => MaybePromise<...>

The handle hook runs every time the SvelteKit server receives a request and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).

Handle
= async ({ event: RequestEvent<Partial<Record<string, string>>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= await
function getUser(sessionid: string | undefined): {
    name: string;
}
getUser
(event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Partial<Record<string, string>>, string | null>event); };
src/routes/account/+page.server
/** @type {import('./$types').PageServerLoad} */
export function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageServerLoad}
load
(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event) {
return {
user: {
    name: string;
} | null
user
: event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
}; } /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>logout: async (event: RequestEvent<Record<string, any>, string | null>event) => { event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.delete(name: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.serialize. The path must match the path of the cookie you want to delete. See documentation here
delete
('sessionid', { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= null;
} };
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad, 
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const const load: PageServerLoadload: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event) => { return {
user: {
    name: string;
} | null
user
: event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
}; }; export const
const actions: {
    logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
actions
= {
logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>logout: async (event: RequestEvent<Record<string, any>, string | null>event) => { event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.delete(name: string, opts: CookieSerializeOptions & {
    path: string;
}): void

Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.serialize. The path must match the path of the cookie you want to delete. See documentation here
delete
('sessionid', { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= null;
} } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

渐进式增强

在前面的章节中,我们构建了一个在没有客户端 JavaScript 的情况下工作/login action —— 没有 fetch。这很好,但当 JavaScript 可用 时,我们可以渐进式增强表单交互,以提供更好的用户体验。

use:enhance

渐进式增强表单的最简单方法是添加 use:enhance action :

src/routes/login/+page
<script>
  import { enhance } from '$app/forms';

  /** @type {{ form: import('./$types').ActionData }} */
  let { form } = $props();
</script>

<form method="POST" use:enhance>
<script lang="ts">
  import { enhance } from '$app/forms';
	import type { ActionData } from './$types';

  let { form }: { form: ActionData } = $props();
</script>

<form method="POST" use:enhance>

use:enhance 只能与 method="POST" 的表单一起使用。它将无法与 method="GET" 一起工作,后者是未指定方法的表单的默认方法。在未指定 method="POST" 的表单上尝试使用 use:enhance 将导致错误。

是的,enhance action 和 <form action> 都叫做 ‘action’,这些文档充满了各种 action。抱歉。

没有参数时,use:enhance 将模拟浏览器原生行为,只是不进行完整页面重载。它将:

  • 在成功或无效响应时更新 form 属性、page.formpage.status,但仅当 action 在您提交的同一页面上时。例如,如果您的表单看起来像 <form action="/somewhere/else" ..>form 属性和 page.form 状态将 不会 更新。这是因为在本地表单提交的情况下,您将被重定向到 action 所在的页面。如果您希望无论如何都能更新,使用 applyAction
  • 重置 <form> 元素
  • 在成功响应时使用 invalidateAll 使所有数据失效
  • 在重定向响应时调用 goto
  • 如果发生错误,渲染最近的 +error 边界
  • 将焦点重置到适当的元素

自定义 use:enhance

要自定义行为,您可以提供一个 SubmitFunction,它会在表单提交前立即运行,并(可选地)返回一个随 ActionResult 一起运行的回调。请注意,如果您返回一个回调,上述默认行为将不会被触发。要恢复默认行为,请调用 update

<form
  method="POST"
  use:enhance={({ formElement, formData, action, cancel, submitter }) => {
	// `formElement` 是这个 `<form>` 元素
	// `formData` 是即将提交的 `FormData` 对象
	// `action` 是表单提交的 URL
	// 调用 `cancel()` 将阻止提交
	// `submitter` 是导致表单提交的 `HTMLElement`

	return async ({ result, update }) => {
	  // `result` 是一个 `ActionResult` 对象
	  // `update` 是一个触发默认逻辑的函数,如果没有设置此回调
	};
  }}
>

您可以使用这些函数来显示和隐藏加载界面等。

如果您返回一个回调,您可能需要重现部分默认的 use:enhance 行为,但在成功响应时不使所有数据失效。您可以使用 applyAction 来实现:

src/routes/login/+page
<script>
  import { enhance, applyAction } from '$app/forms';

  /** @type {{ form: import('./$types').ActionData }} */
  let { form } = $props();
</script>

<form
  method="POST"
  use:enhance={({ formElement, formData, action, cancel }) => {
	return async ({ result }) => {
	  // `result` 是一个 `ActionResult` 对象
			if (result.type === 'redirect') {
		goto(result.location);
	  } else {
		await applyAction(result);
	  }
	};
  }}
>
<script lang="ts">
  import { enhance, applyAction } from '$app/forms';
	import type { ActionData } from './$types';

  let { form }: { form: ActionData } = $props();
</script>

<form
  method="POST"
  use:enhance={({ formElement, formData, action, cancel }) => {
	return async ({ result }) => {
	  // `result` 是一个 `ActionResult` 对象
			if (result.type === 'redirect') {
		goto(result.location);
	  } else {
		await applyAction(result);
	  }
	};
  }}
>

applyAction(result) 的行为取决于 result.type

  • success, failure — 将 page.status 设置为 result.status,并将 formpage.form 更新为 result.data(无论您从哪里提交,这与 enhanceupdate 形成对比)
  • redirect — 调用 goto(result.location, { invalidateAll: true })
  • error — 使用 result.error 渲染最近的 +error 边界

在所有情况下,焦点将被重置

自定义事件监听器

我们也可以不使用 use:enhance,在 <form> 上使用普通的事件监听器,自己实现渐进式增强:

src/routes/login/+page
<script>
  import { invalidateAll, goto } from '$app/navigation';
  import { applyAction, deserialize } from '$app/forms';

  /** @type {{ form: import('./$types').ActionData }} */
  let { form } = $props();

  /** @param {SubmitEvent & { currentTarget: EventTarget & HTMLFormElement}} event */
  async function handleSubmit(event) {
	event.preventDefault();
	const data = new FormData(event.currentTarget);

	const response = await fetch(event.currentTarget.action, {
	  method: 'POST',
	  body: data
	});

	/** @type {import('@sveltejs/kit').ActionResult} */
	const result = deserialize(await response.text());

	if (result.type === 'success') {
	  // 重新运行所有 `load` 函数,跟随成功的更新
	  await invalidateAll();
	}

	applyAction(result);
  }
</script>

<form method="POST" onsubmit={handleSubmit}>
  <!-- content -->
</form>
<script lang="ts">
  import { invalidateAll, goto } from '$app/navigation';
  import { applyAction, deserialize } from '$app/forms';
	import type { ActionData } from './$types';
	import type { ActionResult } from '@sveltejs/kit';

  let { form }: { form: ActionData } = $props();

  async function handleSubmit(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement}) {
	event.preventDefault();
	const data = new FormData(event.currentTarget);

	const response = await fetch(event.currentTarget.action, {
	  method: 'POST',
	  body: data
	});

	const result: ActionResult = deserialize(await response.text());

	if (result.type === 'success') {
	  // 重新运行所有 `load` 函数,跟随成功的更新
	  await invalidateAll();
	}

	applyAction(result);
  }
</script>

<form method="POST" onsubmit={handleSubmit}>
  <!-- content -->
</form>

请注意,在使用 $app/forms 中相应的方法进一步处理响应之前,需要 deserialize 响应。仅 JSON.parse() 是不够的,因为表单 action(如 load 函数)也支持返回 DateBigInt 对象。

如果您在 +page.server.js 旁边有一个 +server.jsfetch 请求将默认路由到那里。要改为 POST+page.server.js 中的 action ,请使用自定义的 x-sveltekit-action 头:

const const response: Responseresponse = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(this.action, {
  RequestInit.method?: string | undefined

A string to set request’s method.

method
: 'POST',
RequestInit.body?: BodyInit | null | undefined

A BodyInit object or null to set request’s body.

body
: data,
RequestInit.headers?: HeadersInit | undefined

A Headers object, an object literal, or an array of two-item arrays to set request’s headers.

headers
: {
'x-sveltekit-action': 'true' } });

替代方案

表单 action 是向服务端发送数据的首选方法,因为它们可以渐进式增强,但您也可以使用 +server.js 文件来公开(例如)一个 JSON API。以下是这种交互的示例:

src/routes/send-message/+page
<script>
  function rerun() {
	fetch('/api/ci', {
	  method: 'POST'
	});
  }
</script>

<button onclick={rerun}>Rerun CI</button>
<script lang="ts">
  function rerun() {
	fetch('/api/ci', {
	  method: 'POST'
	});
  }
</script>

<button onclick={rerun}>Rerun CI</button>
src/routes/api/ci/+server
/** @type {import('./$types').RequestHandler} */
export function function POST(): void
@type{import('./$types').RequestHandler}
POST
() {
// do something }
import type { 
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
} from './$types';
export const const POST: RequestHandlerPOST:
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
= () => {
// do something };

GET 与 POST

如我们所见,要调用表单 action ,必须使用 method="POST"

有些表单不需要向服务端 POST 数据 —— 例如搜索输入。对于这些表单,您可以使用 method="GET"(或等效地,不指定 method),SvelteKit 将像处理 <a> 元素一样处理它们,使用客户端路由而不是完整页面导航:

<form action="/search">
	<label>
		Search
		<input name="q" />
	</label>
</form>

提交此表单将导航到 /search?q=... 并调用您的 load 函数,但不会调用 action 。与 <a> 元素一样,您可以在 <form> 上设置 data-sveltekit-reloaddata-sveltekit-replacestatedata-sveltekit-keepfocus 以及 data-sveltekit-noscroll 属性,以控制路由器的行为。

进一步阅读

在 GitHub 编辑此页面

上一页 下一页