远程函数
自 2.27 起可用
¥Available since 2.27
远程函数是客户端和服务器之间进行类型安全通信的工具。它们可以在应用的任何位置调用,但始终在服务器上运行,这意味着它们可以安全地访问包含环境变量和数据库客户端等内容的 服务器专用模块。
¥Remote functions are a tool for type-safe communication between client and server. They can be called anywhere in your app, but always run on the server, meaning they can safely access server-only modules containing things like environment variables and database clients.
结合 Svelte 对 await 的实验性支持,它允许你直接在组件内部加载和操作数据。
¥Combined with Svelte’s experimental support for await, it allows you to load and manipulate data directly inside your components.
此功能目前处于实验阶段,这意味着它可能包含错误,并且可能会随时更改,恕不另行通知。你必须在 svelte.config.js 中添加 kit.experimental.remoteFunctions 选项,并可选地添加 compilerOptions.experimental.async 选项以在组件中使用 await:
¥This feature is currently experimental, meaning it is likely to contain bugs and is subject to change without notice. You must opt in by adding the kit.experimental.remoteFunctions option in your svelte.config.js and optionally, the compilerOptions.experimental.async option to use await in components:
/** @type {import('@sveltejs/kit').Config} */
const const config: {
kit: {
experimental: {
remoteFunctions: boolean;
};
};
compilerOptions: {
experimental: {
async: boolean;
};
};
}
config = {
kit: {
experimental: {
remoteFunctions: boolean;
};
}
kit: {
experimental: {
remoteFunctions: boolean;
}
experimental: {
remoteFunctions: booleanremoteFunctions: true
}
},
compilerOptions: {
experimental: {
async: boolean;
};
}
compilerOptions: {
experimental: {
async: boolean;
}
experimental: {
async: booleanasync: true
}
}
};
export default const config: {
kit: {
experimental: {
remoteFunctions: boolean;
};
};
compilerOptions: {
experimental: {
async: boolean;
};
};
}
config;概述(Overview)
¥Overview
远程函数从 .remote.js 或 .remote.ts 文件导出,有四种形式:query、form、command 和 prerender。在客户端,导出的函数将转换为 fetch 封装器,并通过生成的 HTTP 端点调用服务器上的对应函数。远程文件必须放置在你的 src 目录中。
¥Remote functions are exported from a .remote.js or .remote.ts file, and come in four flavours: query, form, command and prerender. On the client, the exported functions are transformed to fetch wrappers that invoke their counterparts on the server via a generated HTTP endpoint. Remote files must be placed in your src directory.
query
query 函数允许你从服务器读取动态数据(对于静态数据,请考虑使用 prerender):
¥The query function allows you to read dynamic data from the server (for static data, consider using prerender instead):
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getPosts: RemoteQueryFunction<void, any[]>getPosts = query<any[]>(fn: () => MaybePromise<any[]>): RemoteQueryFunction<void, any[]> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => {
const const posts: any[]posts = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT title, slug
FROM post
ORDER BY published_at
DESC
`;
return const posts: any[]posts;
});在整个页面中,你将看到从
$lib/server/database和$lib/server/auth等虚构模块导入的内容。这些导入纯粹是为了说明目的 - 你可以使用任何你喜欢的数据库客户端和身份验证设置。上面的
db.sql函数是一个 tagged template function,它会转义任何插值。
从 getPosts 返回的查询将作为解析为 posts 的 Promise 元素运行:
¥The query returned from getPosts works as a Promise that resolves to posts:
<script>
import { getPosts } from './data.remote';
</script>
<h1>Recent posts</h1>
<ul>
{#each await getPosts() as { title, slug }}
<li><a href="/blog/{slug}">{title}</a></li>
{/each}
</ul><script lang="ts">
import { getPosts } from './data.remote';
</script>
<h1>Recent posts</h1>
<ul>
{#each await getPosts() as { title, slug }}
<li><a href="/blog/{slug}">{title}</a></li>
{/each}
</ul>在 Promise 解析之前(如果出现错误),将调用最近的 <svelte:boundary>。
¥Until the promise resolves — and if it errors — the nearest <svelte:boundary> will be invoked.
虽然建议使用 await,但查询也可以使用 loading、error 和 current 属性:
¥While using await is recommended, as an alternative the query also has loading, error and current properties:
<script>
import { getPosts } from './data.remote';
const query = getPosts();
</script>
<h1>Recent posts</h1>
{#if query.error}
<p>oops!</p>
{:else if query.loading}
<p>loading...</p>
{:else}
<ul>
{#each query.current as { title, slug }}
<li><a href="/blog/{slug}">{title}</a></li>
{/each}
</ul>
{/if}<script lang="ts">
import { getPosts } from './data.remote';
const query = getPosts();
</script>
<h1>Recent posts</h1>
{#if query.error}
<p>oops!</p>
{:else if query.loading}
<p>loading...</p>
{:else}
<ul>
{#each query.current as { title, slug }}
<li><a href="/blog/{slug}">{title}</a></li>
{/each}
</ul>
{/if}在本文档的其余部分,我们将使用
await形式。
查询参数(Query arguments)
¥Query arguments
查询函数可以接受参数,例如单个帖子的 slug:
¥Query functions can accept an argument, such as the slug of an individual post:
<script>
import { getPost } from '../data.remote';
let { params } = $props();
const post = $derived(await getPost(params.slug));
</script>
<h1>{post.title}</h1>
<div>{@html post.content}</div><script lang="ts">
import { getPost } from '../data.remote';
let { params } = $props();
const post = $derived(await getPost(params.slug));
</script>
<h1>{post.title}</h1>
<div>{@html post.content}</div>由于 getPost 暴露了一个 HTTP 端点,因此验证此参数以确保其类型正确非常重要。为此,我们可以使用任何 标准架构 验证库,例如 Zod 或 Valibot:
¥Since getPost exposes an HTTP endpoint, it’s important to validate this argument to be sure that it’s the correct type. For this, we can use any Standard Schema validation library such as Zod or Valibot:
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error } from '@sveltejs/kit';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, any>getPost = query<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any): RemoteQueryFunction<string, any> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (slug: stringslug) => {
const [const post: anypost] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT * FROM post
WHERE slug = ${slug: stringslug}
`;
if (!const post: anypost) function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(404, 'Not found');
return const post: anypost;
});参数和返回值都使用 devalue 序列化,除了 JSON 之外,它还处理 Date 和 Map 等类型(以及 传输钩子 中定义的自定义类型)。
¥Both the argument and the return value are serialized with devalue, which handles types like Date and Map (and custom types defined in your transport hook) in addition to JSON.
刷新查询(Refreshing queries)
¥Refreshing queries
任何查询都可以通过其 refresh 方法重新获取,该方法从服务器检索最新值:
¥Any query can be re-fetched via its refresh method, which retrieves the latest value from the server:
<button onclick={() => getPosts().refresh()}>
Check for new posts
</button>查询在页面上运行时会被缓存,也就是
getPosts() === getPosts()。这意味着你不需要像const posts = getPosts()这样的引用来更新查询。
query.batch
query.batch 的工作方式与 query 类似,只是它会批量处理在同一宏任务中发生的请求。这解决了所谓的 n+1 问题:例如,每个查询不会导致单独的数据库调用,而是将同时进行的查询组合在一起。
¥query.batch works like query except that it batches requests that happen within the same macrotask. This solves the so-called n+1 problem: rather than each query resulting in a separate database call (for example), simultaneous queries are grouped together.
在服务器上,回调函数接收调用该函数的参数数组。它必须返回 (input: Input, index: number) => Output 形式的函数。SvelteKit 随后会使用每个输入参数调用此方法,以解析各个调用及其结果。
¥On the server, the callback receives an array of the arguments the function was called with. It must return a function of the form (input: Input, index: number) => Output. SvelteKit will then call this with each of the input arguments to resolve the individual calls with their results.
import * as import vv from 'valibot';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getWeather: RemoteQueryFunction<string, any>getWeather = function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query.function query.batch<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (args: string[]) => MaybePromise<(arg: string, idx: number) => any>): RemoteQueryFunction<string, any> (+1 overload)Creates a batch query function that collects multiple calls and executes them in a single request
See Remote functions for full documentation.
batch(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (cities: string[]cities) => {
const const weather: any[]weather = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT * FROM weather
WHERE city = ANY(${cities: string[]cities})
`;
const const lookup: Map<any, any>lookup = new var Map: MapConstructor
new <any, any>(iterable?: Iterable<readonly [any, any]> | null | undefined) => Map<any, any> (+3 overloads)
Map(const weather: any[]weather.Array<any>.map<[any, any]>(callbackfn: (value: any, index: number, array: any[]) => [any, any], thisArg?: any): [any, any][]Calls a defined callback function on each element of an array, and returns an array that contains the results.
map(w: anyw => [w: anyw.city, w: anyw]));
return (city: stringcity) => const lookup: Map<any, any>lookup.Map<any, any>.get(key: any): anyReturns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
get(city: stringcity);
});<script>
import CityWeather from './CityWeather.svelte';
import { getWeather } from './weather.remote.js';
let { cities } = $props();
let limit = $state(5);
</script>
<h2>Weather</h2>
{#each cities.slice(0, limit) as city}
<h3>{city.name}</h3>
<CityWeather weather={await getWeather(city.id)} />
{/each}
{#if cities.length > limit}
<button onclick={() => limit += 5}>
Load more
</button>
{/if}<script lang="ts">
import CityWeather from './CityWeather.svelte';
import { getWeather } from './weather.remote.js';
let { cities } = $props();
let limit = $state(5);
</script>
<h2>Weather</h2>
{#each cities.slice(0, limit) as city}
<h3>{city.name}</h3>
<CityWeather weather={await getWeather(city.id)} />
{/each}
{#if cities.length > limit}
<button onclick={() => limit += 5}>
Load more
</button>
{/if}form
form 函数可以轻松地将数据写入服务器。它需要一个回调函数,该回调函数接收由提交的 FormData 构造的 data……
¥The form function makes it easy to write data to the server. It takes a callback that receives data constructed from the submitted FormData...
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): neverRedirect 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.
Most common status codes:
303 See Other: redirect as a GET request (often used after a form POST request)
307 Temporary Redirect: redirect will keep the request method
308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page
redirect } from '@sveltejs/kit';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query, function form<Output>(fn: () => MaybePromise<Output>): RemoteForm<void, Output> (+2 overloads)Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
import * as module "$lib/server/auth"auth from '$lib/server/auth';
export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, void>getPost = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (slug: stringslug) => { /* ... */ });
export const const createPost: RemoteForm<{
title: string;
content: string;
}, never>
createPost = form<v.ObjectSchema<{
readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
readonly content: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
}, undefined>, never>(validate: v.ObjectSchema<...>, fn: (data: {
...;
}) => Promise<...>): RemoteForm<...> (+2 overloads)
Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{
readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
readonly content: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
}>(entries: {
readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
readonly content: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
}): v.ObjectSchema<...> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({
title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>title: import vv.pipe<v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>>(schema: v.StringSchema<undefined>, item1: v.NonEmptyAction<string, undefined> | v.PipeAction<string, string, v.NonEmptyIssue<...>>): v.SchemaWithPipe<...> (+20 overloads)
export pipe
Adds a pipeline to a schema, that can validate and transform its input.
pipe(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), import vv.nonEmpty<string>(): v.NonEmptyAction<string, undefined> (+1 overload)
export nonEmpty
Creates a non-empty validation action.
nonEmpty()),
content: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>content:import vv.pipe<v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>>(schema: v.StringSchema<undefined>, item1: v.NonEmptyAction<string, undefined> | v.PipeAction<string, string, v.NonEmptyIssue<...>>): v.SchemaWithPipe<...> (+20 overloads)
export pipe
Adds a pipeline to a schema, that can validate and transform its input.
pipe(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), import vv.nonEmpty<string>(): v.NonEmptyAction<string, undefined> (+1 overload)
export nonEmpty
Creates a non-empty validation action.
nonEmpty())
}),
async ({ title: stringtitle, content: stringcontent }) => {
// Check the user is logged in
const const user: auth.User | nulluser = await module "$lib/server/auth"auth.function getUser(): Promise<auth.User | null>Gets a user’s info from their cookies, using getRequestEvent
getUser();
if (!const user: auth.User | nulluser) function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(401, 'Unauthorized');
const const slug: stringslug = title: stringtitle.String.toLowerCase(): stringConverts all the alphabetic characters in a string to lowercase.
toLowerCase().String.replace(searchValue: {
[Symbol.replace](string: string, replaceValue: string): string;
}, replaceValue: string): string (+3 overloads)
Passes a string and
{@linkcode
replaceValue
}
to the [Symbol.replace] method on
{@linkcode
searchValue
}
. This method is expected to implement its own replacement algorithm.
replace(/ /g, '-');
// Insert into the database
await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
INSERT INTO post (slug, title, content)
VALUES (${const slug: stringslug}, ${title: stringtitle}, ${content: stringcontent})
`;
// Redirect to the newly created page
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): neverRedirect 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.
Most common status codes:
303 See Other: redirect as a GET request (often used after a form POST request)
307 Temporary Redirect: redirect will keep the request method
308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page
redirect(303, `/blog/${const slug: stringslug}`);
}
);...并返回一个可以展开到 <form> 元素上的对象。每当表单提交时,都会调用回调。
¥...and returns an object that can be spread onto a <form> element. The callback is called whenever the form is submitted.
<script>
import { createPost } from '../data.remote';
</script>
<h1>Create a new post</h1>
<form {...createPost}>
<!-- form content goes here -->
<button>Publish!</button>
</form><script lang="ts">
import { createPost } from '../data.remote';
</script>
<h1>Create a new post</h1>
<form {...createPost}>
<!-- form content goes here -->
<button>Publish!</button>
</form>表单对象包含 method 和 action 属性,使其无需 JavaScript 即可工作(即提交数据并重新加载页面)。它还有一个 attachment,可以在 JavaScript 可用时逐步增强表单,无需重新加载整个页面即可提交数据。
¥The form object contains method and action properties that allow it to work without JavaScript (i.e. it submits data and reloads the page). It also has an attachment that progressively enhances the form when JavaScript is available, submitting data without reloading the entire page.
与 query 一样,如果回调使用已提交的 data,则应通过将 标准架构 作为第一个参数传递给 form 来实现 validated。
¥As with query, if the callback uses the submitted data, it should be validated by passing a Standard Schema as the first argument to form.
字段(Fields)
¥Fields
表单由一组由架构定义的字段组成。对于 createPost,我们有两个字段,title 和 content,它们都是字符串。要获取字段的属性,请调用其 .as(...) 方法,并指定要使用的 输入类型:
¥A form is composed of a set of fields, which are defined by the schema. In the case of createPost, we have two fields, title and content, which are both strings. To get the attributes for a field, call its .as(...) method, specifying which input type to use:
<form {...createPost}>
<label>
<h2>Title</h2>
<input {...createPost.fields.title.as('text')} />
</label>
<label>
<h2>Write your post</h2>
<textarea {...createPost.fields.content.as('text')}></textarea>
</label>
<button>Publish!</button>
</form>这些属性允许 SvelteKit 设置正确的输入类型,设置用于构造传递给处理程序的 data 的 name,填充表单的 value(例如,在提交失败后,避免用户重新输入所有内容),并设置 aria-invalid 状态。
¥These attributes allow SvelteKit to set the correct input type, set a name that is used to construct the data passed to the handler, populate the value of the form (for example following a failed submission, to save the user having to re-enter everything), and set the aria-invalid state.
字段可以嵌套在对象和数组中,其值可以是字符串、数字、布尔值或 File 对象。例如,如果你的架构如下所示……
¥Fields can be nested in objects and arrays, and their values can be strings, numbers, booleans or File objects. For example, if your schema looked like this...
const const datingProfile: v.ObjectSchema<{
readonly name: v.StringSchema<undefined>;
readonly photo: v.FileSchema<undefined>;
readonly info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>;
readonly attributes: v.ArraySchema<...>;
}, undefined>
datingProfile = import vv.object<{
readonly name: v.StringSchema<undefined>;
readonly photo: v.FileSchema<undefined>;
readonly info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>;
readonly attributes: v.ArraySchema<...>;
}>(entries: {
readonly name: v.StringSchema<undefined>;
readonly photo: v.FileSchema<undefined>;
readonly info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>;
readonly attributes: v.ArraySchema<...>;
}): v.ObjectSchema<...> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({
name: v.StringSchema<undefined>name: import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(),
photo: v.FileSchema<undefined>photo: import vv.function file(): v.FileSchema<undefined> (+1 overload)
export file
Creates a file schema.
file(),
info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>
info: import vv.object<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}>(entries: {
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}): v.ObjectSchema<...> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({
height: v.NumberSchema<undefined>height: import vv.function number(): v.NumberSchema<undefined> (+1 overload)
export number
Creates a number schema.
number(),
likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>likesDogs: import vv.optional<v.BooleanSchema<undefined>, false>(wrapped: v.BooleanSchema<undefined>, default_: false): v.OptionalSchema<v.BooleanSchema<undefined>, false> (+1 overload)
export optional
Creates an optional schema.
optional(import vv.function boolean(): v.BooleanSchema<undefined> (+1 overload)
export boolean
Creates a boolean schema.
boolean(), false)
}),
attributes: v.ArraySchema<v.StringSchema<undefined>, undefined>attributes: import vv.array<v.StringSchema<undefined>>(item: v.StringSchema<undefined>): v.ArraySchema<v.StringSchema<undefined>, undefined> (+1 overload)
export array
Creates an array schema.
array(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string())
});
export const const createProfile: RemoteForm<{
name: string;
photo: File;
info: {
height: number;
likesDogs?: boolean | undefined;
};
attributes: string[];
}, void>
createProfile = form<v.ObjectSchema<{
readonly name: v.StringSchema<undefined>;
readonly photo: v.FileSchema<undefined>;
readonly info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>;
readonly attributes: v.ArraySchema<...>;
}, undefined>, void>(validate: v.ObjectSchema<...>, fn: (data: {
...;
}) => MaybePromise<...>): RemoteForm<...> (+2 overloads)
Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(const datingProfile: v.ObjectSchema<{
readonly name: v.StringSchema<undefined>;
readonly photo: v.FileSchema<undefined>;
readonly info: v.ObjectSchema<{
readonly height: v.NumberSchema<undefined>;
readonly likesDogs: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
}, undefined>;
readonly attributes: v.ArraySchema<...>;
}, undefined>
datingProfile, (data: {
name: string;
photo: File;
info: {
height: number;
likesDogs: boolean;
};
attributes: string[];
}
data) => { /* ... */ });...你的表单可能如下所示:
¥...your form could look like this:
<script>
import { createProfile } from './data.remote';
const { name, photo, info, attributes } = createProfile.fields;
</script>
<form {...createProfile} enctype="multipart/form-data">
<label>
<input {...name.as('text')} /> Name
</label>
<label>
<input {...photo.as('file')} /> Photo
</label>
<label>
<input {...info.height.as('number')} /> Height (cm)
</label>
<label>
<input {...info.likesDogs.as('checkbox')} /> I like dogs
</label>
<h2>My best attributes</h2>
<input {...attributes[0].as('text')} />
<input {...attributes[1].as('text')} />
<input {...attributes[2].as('text')} />
<button>submit</button>
</form>由于我们的表单包含 file 输入,因此我们添加了 enctype="multipart/form-data" 属性。info.height 和 info.likesDogs 的值分别被强制转换为数字和布尔值。
¥Because our form contains a file input, we’ve added an enctype="multipart/form-data" attribute. The values for info.height and info.likesDogs are coerced to a number and a boolean respectively.
如果
checkbox输入未选中,则该值不会包含在 SvelteKit 构建数据的FormData对象中。因此,我们必须在架构中将该值设为可选。在 Valibot 中,这意味着使用v.optional(v.boolean(), false)而不是v.boolean(),而在 Zod 中,这意味着使用z.coerce.boolean<boolean>()。
如果 radio 和 checkbox 输入都属于同一字段,则必须将 value 指定为 .as(...) 的第二个参数:
¥In the case of radio and checkbox inputs that all belong to the same field, the value must be specified as a second argument to .as(...):
export const const survey: RemoteForm<{
operatingSystem: "windows" | "mac" | "linux";
languages?: ("html" | "css" | "js")[] | undefined;
}, void>
survey = form<v.ObjectSchema<{
readonly operatingSystem: v.PicklistSchema<["windows", "mac", "linux"], undefined>;
readonly languages: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, readonly []>;
}, undefined>, void>(validate: v.ObjectSchema<...>, fn: (data: {
...;
}) => MaybePromise<...>): RemoteForm<...> (+2 overloads)
Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{
readonly operatingSystem: v.PicklistSchema<["windows", "mac", "linux"], undefined>;
readonly languages: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, readonly []>;
}>(entries: {
readonly operatingSystem: v.PicklistSchema<["windows", "mac", "linux"], undefined>;
readonly languages: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, readonly []>;
}): v.ObjectSchema<...> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({
operatingSystem: v.PicklistSchema<["windows", "mac", "linux"], undefined>operatingSystem: import vv.picklist<["windows", "mac", "linux"]>(options: ["windows", "mac", "linux"]): v.PicklistSchema<["windows", "mac", "linux"], undefined> (+1 overload)
export picklist
Creates a picklist schema.
picklist(['windows', 'mac', 'linux']),
languages: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, readonly []>languages: import vv.optional<v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, readonly []>(wrapped: v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined>, default_: readonly []): v.OptionalSchema<...> (+1 overload)
export optional
Creates an optional schema.
optional(import vv.array<v.PicklistSchema<["html", "css", "js"], undefined>>(item: v.PicklistSchema<["html", "css", "js"], undefined>): v.ArraySchema<v.PicklistSchema<["html", "css", "js"], undefined>, undefined> (+1 overload)
export array
Creates an array schema.
array(import vv.picklist<["html", "css", "js"]>(options: ["html", "css", "js"]): v.PicklistSchema<["html", "css", "js"], undefined> (+1 overload)
export picklist
Creates a picklist schema.
picklist(['html', 'css', 'js'])), [])
}),
(data: {
operatingSystem: "windows" | "mac" | "linux";
languages: ("html" | "css" | "js")[];
}
data) => { /* ... */ }
);<form {...survey}>
<h2>Which operating system do you use?</h2>
{#each ['windows', 'mac', 'linux'] as os}
<label>
<input {...survey.fields.operatingSystem.as('radio', os)}>
{os}
</label>
{/each}
<h2>Which languages do you write code in?</h2>
{#each ['html', 'css', 'js'] as language}
<label>
<input {...survey.fields.languages.as('checkbox', language)}>
{language}
</label>
{/each}
<button>submit</button>
</form>或者,你可以使用 select 和 select multiple:
¥Alternatively, you could use select and select multiple:
<form {...survey}>
<h2>Which operating system do you use?</h2>
<select {...survey.fields.operatingSystem.as('select')}>
<option>windows</option>
<option>mac</option>
<option>linux</option>
</select>
<h2>Which languages do you write code in?</h2>
<select {...survey.fields.languages.as('select multiple')}>
<option>html</option>
<option>css</option>
<option>js</option>
</select>
<button>submit</button>
</form>与未选中的
checkbox输入一样,如果未进行任何选择,则数据将为undefined。因此,languages字段使用v.optional(v.array(...), [])而不是v.array(...)。
程序化验证(Programmatic validation)
¥Programmatic validation
除了声明式架构验证之外,你还可以使用 invalid 函数在表单处理程序中以编程方式将字段标记为无效。这在你尝试执行某些操作之前无法确定某些内容是否有效的情况下非常有用:
¥In addition to declarative schema validation, you can programmatically mark fields as invalid inside the form handler using the invalid function. This is useful for cases where you can’t know if something is valid until you try to perform some action:
import * as import vv from 'valibot';
import { function form<Output>(fn: () => MaybePromise<Output>): RemoteForm<void, Output> (+2 overloads)Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form } from '$app/server';
import * as import dbdb from '$lib/server/database';
export const const buyHotcakes: RemoteForm<RemoteFormInput, unknown>buyHotcakes = form<RemoteFormInput, unknown>(validate: "unchecked", fn: (data: RemoteFormInput) => unknown): RemoteForm<RemoteFormInput, unknown> (+2 overloads)Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{
readonly qty: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 1, "you must buy at least one hotcake">]>;
}>(entries: {
readonly qty: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 1, "you must buy at least one hotcake">]>;
}): v.ObjectSchema<...> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({
qty: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 1, "you must buy at least one hotcake">]>qty: import vv.pipe<v.NumberSchema<undefined>, v.MinValueAction<number, 1, "you must buy at least one hotcake">>(schema: v.NumberSchema<undefined>, item1: v.MinValueAction<number, 1, "you must buy at least one hotcake"> | v.PipeAction<...>): v.SchemaWithPipe<...> (+20 overloads)
export pipe
Adds a pipeline to a schema, that can validate and transform its input.
pipe(
import vv.function number(): v.NumberSchema<undefined> (+1 overload)
export number
Creates a number schema.
number(),
import vv.minValue<number, 1, "you must buy at least one hotcake">(requirement: 1, message: "you must buy at least one hotcake"): v.MinValueAction<number, 1, "you must buy at least one hotcake"> (+1 overload)
export minValue
Creates a min value validation action.
minValue(1, 'you must buy at least one hotcake')
)
}),
async (data: anydata, invalid: anyinvalid) => {
try {
await import dbdb.buy(data: anydata.qty);
} catch (function (local var) e: unknowne) {
if (function (local var) e: unknowne.code === 'OUT_OF_STOCK') {
invalid: anyinvalid(
invalid: anyinvalid.qty(`we don't have enough hotcakes`)
);
}
}
}
);invalid 函数既可以作为函数,也可以作为代理:
¥The invalid function works as both a function and a proxy:
调用
invalid(issue1, issue2, ...issueN)抛出验证错误¥Call
invalid(issue1, issue2, ...issueN)to throw a validation error如果问题是
string,它将应用于整个表单(并将显示在fields.allIssues()中)¥If an issue is a
string, it applies to the form as a whole (and will show up infields.allIssues())使用
invalid.fieldName(message)为特定字段创建问题。与fields类似,这也是类型安全的,你可以使用常规属性访问语法为深度嵌套对象(例如invalid.profile.email('Email already exists')或invalid.items[0].qty('Insufficient stock'))创建问题。¥Use
invalid.fieldName(message)to create an issue for a specific field. Likefieldsthis is type-safe and you can use regular property access syntax to create issues for deeply nested objects (e.g.invalid.profile.email('Email already exists')orinvalid.items[0].qty('Insufficient stock'))
验证(Validation)
¥Validation
如果提交的数据未通过架构,则回调将不会运行。相反,每个无效字段的 issues() 方法将返回一个 { message: string } 对象数组,并且 aria-invalid 属性(从 as(...) 返回)将设置为 true:
¥If the submitted data doesn’t pass the schema, the callback will not run. Instead, each invalid field’s issues() method will return an array of { message: string } objects, and the aria-invalid attribute (returned from as(...)) will be set to true:
<form {...createPost}>
<label>
<h2>Title</h2>
{#each createPost.fields.title.issues() as issue}
<p class="issue">{issue.message}</p>
{/each}
<input {...createPost.fields.title.as('text')} />
</label>
<label>
<h2>Write your post</h2>
{#each createPost.fields.content.issues() as issue}
<p class="issue">{issue.message}</p>
{/each}
<textarea {...createPost.fields.content.as('text')}></textarea>
</label>
<button>Publish!</button>
</form>你无需等到表单提交后再验证数据 - 你可以通过编程方式调用 validate(),例如在 oninput 回调(每次按键时都会验证数据)或 onchange 回调中:
¥You don’t need to wait until the form is submitted to validate the data — you can call validate() programmatically, for example in an oninput callback (which will validate the data on every keystroke) or an onchange callback:
<form {...createPost} oninput={() => createPost.validate()}>
<!-- -->
</form>默认情况下,如果问题属于尚未交互的表单控件,则会被忽略。要验证所有输入,请调用 validate({ includeUntouched: true })。
¥By default, issues will be ignored if they belong to form controls that haven’t yet been interacted with. To validate all inputs, call validate({ includeUntouched: true }).
对于客户端验证,你可以指定一个预检模式 (preflight schema),该模式将填充 issues(),并在数据未通过验证时阻止将数据发送到服务器:
¥For client-side validation, you can specify a preflight schema which will populate issues() and prevent data being sent to the server if the data doesn’t validate:
<script>
import * as v from 'valibot';
import { createPost } from '../data.remote';
const schema = v.object({
title: v.pipe(v.string(), v.nonEmpty()),
content: v.pipe(v.string(), v.nonEmpty())
});
</script>
<h1>Create a new post</h1>
<form {...createPost.preflight(schema)}>
<!-- -->
</form>如果适用,预检模式可以与服务器端模式是同一个对象,但它无法像 ‘this value already exists in the database’ 那样执行服务器端检查。请注意,你无法从
.remote.ts或.remote.js文件导出模式,因此必须从共享模块或包含<form>的组件中的<script module>块导出模式。
要获取所有问题的列表,而不仅仅是属于单个字段的问题,你可以使用 fields.allIssues() 方法:
¥To get a list of all issues, rather than just those belonging to a single field, you can use the fields.allIssues() method:
{#each createPost.fields.allIssues() as issue}
<p>{issue.message}</p>
{/each}获取/设置输入(Getting/setting inputs)
¥Getting/setting inputs
每个字段都有一个 value() 方法,用于反映其当前值。当用户与表单交互时,表单会自动更新:
¥Each field has a value() method that reflects its current value. As the user interacts with the form, it is automatically updated:
<form {...createPost}>
<!-- -->
</form>
<div class="preview">
<h2>{createPost.fields.title.value()}</h2>
<div>{@html render(createPost.fields.content.value())}</div>
</div>或者,createPost.fields.value() 会返回一个 { title, content } 对象。
¥Alternatively, createPost.fields.value() would return a { title, content } object.
你可以通过 set(...) 方法更新一个字段(或一组字段):
¥You can update a field (or a collection of fields) via the set(...) method:
<script>
import { createPost } from '../data.remote';
// this...
createPost.fields.set({
title: 'My new blog post',
content: 'Lorem ipsum dolor sit amet...'
});
// ...is equivalent to this:
createPost.fields.title.set('My new blog post');
createPost.fields.content.set('Lorem ipsum dolor sit amet');
</script>处理敏感数据(Handling sensitive data)
¥Handling sensitive data
对于非渐进增强的表单提交(即由于某种原因 JavaScript 不可用),如果提交的数据无效,value() 也会被填充,这样用户就无需从头开始填写整个表单。
¥In the case of a non-progressively-enhanced form submission (i.e. where JavaScript is unavailable, for whatever reason) value() is also populated if the submitted data is invalid, so that the user does not need to fill the entire form out from scratch.
你可以使用带有前导下划线的名称来防止敏感数据(例如密码和信用卡号)被发送回用户:
¥You can prevent sensitive data (such as passwords and credit card numbers) from being sent back to the user by using a name with a leading underscore:
<form {...register}>
<label>
Username
<input {...register.fields.username.as('text')} />
</label>
<label>
Password
<input {...register.fields._password.as('password')} />
</label>
<button>Sign up!</button>
</form>在此示例中,如果数据未通过验证,则页面重新加载时只会填充第一个 <input>。
¥In this example, if the data does not validate, only the first <input> will be populated when the page reloads.
单次修改(Single-flight mutations)
¥Single-flight mutations
默认情况下,页面上使用的所有查询(以及任何 load 函数)都是表单提交成功后自动刷新。这确保所有内容都是最新的,但效率也较低:许多查询将保持不变,并且需要再次访问服务器才能获取更新的数据。
¥By default, all queries used on the page (along with any load functions) are automatically refreshed following a successful form submission. This ensures that everything is up-to-date, but it’s also inefficient: many queries will be unchanged, and it requires a second trip to the server to get the updated data.
相反,我们可以指定哪些查询应该在响应特定表单提交时刷新。这称为单次飞行突变,有两种方法可以实现它。第一个函数是在表单处理程序内部刷新服务器上的查询:
¥Instead, we can specify which queries should be refreshed in response to a particular form submission. This is called a single-flight mutation, and there are two ways to achieve it. The first is to refresh the query on the server, inside the form handler:
export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, void>getPost = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (slug: stringslug) => { /* ... */ });
export const const createPost: RemoteForm<{}, never>createPost = form<v.ObjectSchema<{}, undefined>, never>(validate: v.ObjectSchema<{}, undefined>, fn: (data: {}) => Promise<never>): RemoteForm<{}, never> (+2 overloads)Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{}>(entries: {}): v.ObjectSchema<{}, undefined> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({/* ... */}),
async (data: {}data) => {
// form logic goes here...
// Refresh `getPosts()` on the server, and send
// the data back with the result of `createPost`
await const getPosts: (arg: void) => RemoteQuery<void>getPosts().function refresh(): Promise<void>On the client, this function will re-fetch the query from the server.
On the server, this can be called in the context of a command or form and the refreshed data will accompany the action response back to the client.
This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.
refresh();
// Redirect to the newly created page
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): neverRedirect 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.
Most common status codes:
303 See Other: redirect as a GET request (often used after a form POST request)
307 Temporary Redirect: redirect will keep the request method
308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page
redirect(303, `/blog/${const slug: ""slug}`);
}
);
export const const updatePost: RemoteForm<{}, void>updatePost = form<v.ObjectSchema<{}, undefined>, void>(validate: v.ObjectSchema<{}, undefined>, fn: (data: {}) => MaybePromise<void>): RemoteForm<{}, void> (+2 overloads)Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{}>(entries: {}): v.ObjectSchema<{}, undefined> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({/* ... */}),
async (data: {}data) => {
// form logic goes here...
const const result: anyresult = const externalApi: anyexternalApi.update(const post: {
id: string;
}
post);
// The API already gives us the updated post,
// no need to refresh it, we can set it directly
await const getPost: (arg: string) => RemoteQuery<void>getPost(const post: {
id: string;
}post.id: stringid).function set(value: void): voidOn the client, this function will update the value of the query without re-fetching it.
On the server, this can be called in the context of a command or form and the specified data will accompany the action response back to the client.
const result: anyresult);
}
);第二个函数是从客户端驱动单次飞行突变,我们将在 enhance 部分中看到这一点。
¥The second is to drive the single-flight mutation from the client, which we’ll see in the section on enhance.
返回和重定向(Returns and redirects)
¥Returns and redirects
上述示例使用了 redirect(...) 元素,它将用户引导至新创建的页面。或者,回调可以返回数据,在这种情况下,它将作为 createPost.result 使用:
¥The example above uses redirect(...), which sends the user to the newly created page. Alternatively, the callback could return data, in which case it would be available as createPost.result:
export const const createPost: RemoteForm<{}, {
success: boolean;
}>
createPost = form<v.ObjectSchema<{}, undefined>, {
success: boolean;
}>(validate: v.ObjectSchema<{}, undefined>, fn: (data: {}) => MaybePromise<{
success: boolean;
}>): RemoteForm<{}, {
success: boolean;
}> (+2 overloads)
Creates a form object that can be spread onto a <form> element.
See Remote functions for full documentation.
form(
import vv.object<{}>(entries: {}): v.ObjectSchema<{}, undefined> (+1 overload)
export object
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
object({/* ... */}),
async (data: {}data) => {
// ...
return { success: booleansuccess: true };
}
);<script>
import { createPost } from '../data.remote';
</script>
<h1>Create a new post</h1>
<form {...createPost}>
<!-- -->
</form>
{#if createPost.result?.success}
<p>Successfully published!</p>
{/if}<script lang="ts">
import { createPost } from '../data.remote';
</script>
<h1>Create a new post</h1>
<form {...createPost}>
<!-- -->
</form>
{#if createPost.result?.success}
<p>Successfully published!</p>
{/if}此值是短暂的 - 如果你重新提交、离开或重新加载页面,它将消失。
¥This value is ephemeral — it will vanish if you resubmit, navigate away, or reload the page.
result值不一定表示成功 - 它也可以包含验证错误,以及任何需要在页面重新加载时重新填充表单的数据。
如果提交过程中出现错误,则会渲染最近的 +error.svelte 页面。
¥If an error occurs during submission, the nearest +error.svelte page will be rendered.
enhance
我们可以使用 enhance 方法自定义表单提交时的操作:
¥We can customize what happens when the form is submitted with the enhance method:
<script>
import { createPost } from '../data.remote';
import { showToast } from '$lib/toast';
</script>
<h1>Create a new post</h1>
<form {...createPost.enhance(async ({ form, data, submit }) => {
try {
await submit();
form.reset();
showToast('Successfully published!');
} catch (error) {
showToast('Oh no! Something went wrong');
}
})}>
<!-- -->
</form><script lang="ts">
import { createPost } from '../data.remote';
import { showToast } from '$lib/toast';
</script>
<h1>Create a new post</h1>
<form {...createPost.enhance(async ({ form, data, submit }) => {
try {
await submit();
form.reset();
showToast('Successfully published!');
} catch (error) {
showToast('Oh no! Something went wrong');
}
})}>
<!-- -->
</form>使用
enhance时,<form>不会自动重置 - 如果你想清除输入,必须调用form.reset()。
回调函数接收 form 元素、其包含的 data 元素以及 submit 函数。
¥The callback receives the form element, the data it contains, and a submit function.
要启用客户端驱动的 单次修改,请使用 submit().updates(...)。例如,如果此页面使用了 getPosts() 查询,我们可以像这样刷新它:
¥To enable client-driven single-flight mutations, use submit().updates(...). For example, if the getPosts() query was used on this page, we could refresh it like so:
await function submit(): Promise<any> & {
updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>;
}
submit().function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>updates(function getPosts(): RemoteQuery<Post[]>getPosts());我们还可以在提交过程中覆盖当前数据:
¥We can also override the current data while the submission is ongoing:
await function submit(): Promise<any> & {
updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>;
}
submit().function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>updates(
function getPosts(): RemoteQuery<Post[]>getPosts().function withOverride(update: (current: Post[]) => Post[]): RemoteQueryOverrideTemporarily override the value of a query. This is used with the updates method of a command or enhanced form submission to provide optimistic updates.
<script>
import { getTodos, addTodo } from './todos.remote.js';
const todos = getTodos();
</script>
<form {...addTodo.enhance(async ({ data, submit }) => {
await submit().updates(
todos.withOverride((todos) => [...todos, { text: data.get('text') }])
);
})}>
<input type="text" name="text" />
<button type="submit">Add Todo</button>
</form>
withOverride((posts: Post[]posts) => [const newPost: PostnewPost, ...posts: Post[]posts])
);覆盖函数将立即应用,并在提交完成(或失败)时释放。
¥The override will be applied immediately, and released when the submission completes (or fails).
表单的多个实例(Multiple instances of a form)
¥Multiple instances of a form
某些表单可能会重复出现在列表中。在这种情况下,你可以通过 for(id) 创建单独的表单函数实例来实现隔离。
¥Some forms may be repeated as part of a list. In this case you can create separate instances of a form function via for(id) to achieve isolation.
<script>
import { getTodos, modifyTodo } from '../data.remote';
</script>
<h1>Todos</h1>
{#each await getTodos() as todo}
{@const modify = modifyTodo.for(todo.id)}
<form {...modify}>
<!-- -->
<button disabled={!!modify.pending}>save changes</button>
</form>
{/each}<script lang="ts">
import { getTodos, modifyTodo } from '../data.remote';
</script>
<h1>Todos</h1>
{#each await getTodos() as todo}
{@const modify = modifyTodo.for(todo.id)}
<form {...modify}>
<!-- -->
<button disabled={!!modify.pending}>save changes</button>
</form>
{/each}buttonProps
默认情况下,提交表单会向 <form> 元素的 action 属性指定的 URL 发送请求,对于远程函数来说,该属性是 SvelteKit 生成的表单对象的一个属性。
¥By default, submitting a form will send a request to the URL indicated by the <form> element’s action attribute, which in the case of a remote function is a property on the form object generated by SvelteKit.
<form> 中的 <button> 可以使用 formaction 属性将请求发送到不同的 URL。例如,你可能有一个表单,允许你根据点击的按钮登录或注册。
¥It’s possible for a <button> inside the <form> to send the request to a different URL, using the formaction attribute. For example, you might have a single form that allows you to log in or register depending on which button was clicked.
此属性存在于表单对象的 buttonProps 属性中:
¥This attribute exists on the buttonProps property of a form object:
<script>
import { login, register } from '$lib/auth';
</script>
<form {...login}>
<label>
Your username
<input {...login.fields.username.as('text')} />
</label>
<label>
Your password
<input {...login.fields._password.as('password')} />
</label>
<button>login</button>
<button {...register.buttonProps}>register</button>
</form><script lang="ts">
import { login, register } from '$lib/auth';
</script>
<form {...login}>
<label>
Your username
<input {...login.fields.username.as('text')} />
</label>
<label>
Your password
<input {...login.fields._password.as('password')} />
</label>
<button>login</button>
<button {...register.buttonProps}>register</button>
</form>与表单对象本身一样,buttonProps 也有一个用于自定义提交行为的 enhance 方法。
¥Like the form object itself, buttonProps has an enhance method for customizing submission behaviour.
command
command 函数与 form 类似,允许你将数据写入服务器。与 form 不同,它不特定于某个元素,可以从任何地方调用。
¥The command function, like form, allows you to write data to the server. Unlike form, it’s not specific to an element and can be called from anywhere.
尽可能优先使用
form,因为如果 JavaScript 被禁用或加载失败,它会优雅地降级。
与 query 和 form 一样,如果函数接受参数,则应通过将 标准架构 作为第一个参数传递给 command 来实现 validated。
¥As with query and form, if the function accepts an argument, it should be validated by passing a Standard Schema as the first argument to command.
import * as import vv from 'valibot';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query, function command<Output>(fn: () => Output): RemoteCommand<void, Output> (+2 overloads)Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
command } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getLikes: RemoteQueryFunction<string, any>getLikes = query<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any): RemoteQueryFunction<string, any> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (id: stringid) => {
const [const row: anyrow] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT likes
FROM item
WHERE id = ${id: stringid}
`;
return const row: anyrow.likes;
});
export const const addLike: RemoteCommand<string, Promise<void>>addLike = command<v.StringSchema<undefined>, Promise<void>>(validate: v.StringSchema<undefined>, fn: (arg: string) => Promise<void>): RemoteCommand<string, Promise<void>> (+2 overloads)Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
command(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (id: stringid) => {
await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
UPDATE item
SET likes = likes + 1
WHERE id = ${id: stringid}
`;
});现在只需从(例如)事件处理程序调用 addLike 即可:
¥Now simply call addLike, from (for example) an event handler:
<script>
import { getLikes, addLike } from './likes.remote';
import { showToast } from '$lib/toast';
let { item } = $props();
</script>
<button
onclick={async () => {
try {
await addLike(item.id);
} catch (error) {
showToast('Something went wrong!');
}
}}
>
add like
</button>
<p>likes: {await getLikes(item.id)}</p><script lang="ts">
import { getLikes, addLike } from './likes.remote';
import { showToast } from '$lib/toast';
let { item } = $props();
</script>
<button
onclick={async () => {
try {
await addLike(item.id);
} catch (error) {
showToast('Something went wrong!');
}
}}
>
add like
</button>
<p>likes: {await getLikes(item.id)}</p>渲染期间无法调用命令。
更新查询(Updating queries)
¥Updating queries
要更新 getLikes(item.id) 或任何其他查询,我们需要告诉 SvelteKit 哪些查询需要刷新(与 form 不同,form 默认情况下会使所有内容无效,以近似于原生表单提交的行为)。
¥To update getLikes(item.id), or any other query, we need to tell SvelteKit which queries need to be refreshed (unlike form, which by default invalidates everything, to approximate the behaviour of a native form submission).
我们要么在命令本身内部执行此操作……
¥We either do that inside the command itself...
export const const getLikes: RemoteQueryFunction<string, void>getLikes = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (id: stringid) => { /* ... */ });
export const const addLike: RemoteCommand<string, Promise<void>>addLike = command<v.StringSchema<undefined>, Promise<void>>(validate: v.StringSchema<undefined>, fn: (arg: string) => Promise<void>): RemoteCommand<string, Promise<void>> (+2 overloads)Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
command(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (id: stringid) => {
await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
UPDATE item
SET likes = likes + 1
WHERE id = ${id: stringid}
`;
const getLikes: (arg: string) => RemoteQuery<void>getLikes(id: stringid).function refresh(): Promise<void>On the client, this function will re-fetch the query from the server.
On the server, this can be called in the context of a command or form and the refreshed data will accompany the action response back to the client.
This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.
refresh();
// Just like within form functions you can also do
// getLikes(id).set(...)
// in case you have the result already
});...或者当我们调用它时:
¥...or when we call it:
try {
await const addLike: (arg: string) => Promise<void> & {
updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<...>;
}
addLike(const item: Itemitem.Item.id: stringid).function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<void>updates(const getLikes: (arg: string) => RemoteQuery<number>getLikes(const item: Itemitem.Item.id: stringid));
} catch (var error: unknownerror) {
function showToast(message: string): voidshowToast('Something went wrong!');
}和以前一样,我们可以使用 withOverride 进行乐观更新:
¥As before, we can use withOverride for optimistic updates:
try {
await const addLike: (arg: string) => Promise<void> & {
updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<...>;
}
addLike(const item: Itemitem.Item.id: stringid).function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<void>updates(
const getLikes: (arg: string) => RemoteQuery<number>getLikes(const item: Itemitem.Item.id: stringid).function withOverride(update: (current: number) => number): RemoteQueryOverrideTemporarily override the value of a query. This is used with the updates method of a command or enhanced form submission to provide optimistic updates.
<script>
import { getTodos, addTodo } from './todos.remote.js';
const todos = getTodos();
</script>
<form {...addTodo.enhance(async ({ data, submit }) => {
await submit().updates(
todos.withOverride((todos) => [...todos, { text: data.get('text') }])
);
})}>
<input type="text" name="text" />
<button type="submit">Add Todo</button>
</form>
withOverride((n: numbern) => n: numbern + 1)
);
} catch (var error: unknownerror) {
function showToast(message: string): voidshowToast('Something went wrong!');
}prerender
prerender 函数与 query 类似,不同之处在于它会在构建时调用以预渲染结果。用于每次重新部署最多更改一次的数据。
¥The prerender function is similar to query, except that it will be invoked at build time to prerender the result. Use this for data that changes at most once per redeployment.
import { function prerender<Output>(fn: () => MaybePromise<Output>, options?: {
inputs?: RemotePrerenderInputsGenerator<void>;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<void, Output> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getPosts: RemotePrerenderFunction<void, any[]>getPosts = prerender<any[]>(fn: () => MaybePromise<any[]>, options?: {
inputs?: RemotePrerenderInputsGenerator<void>;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(async () => {
const const posts: any[]posts = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT title, slug
FROM post
ORDER BY published_at
DESC
`;
return const posts: any[]posts;
});你可以在动态页面上使用 prerender 函数,从而允许部分预渲染数据。这将带来非常快速的导航,因为预渲染数据可以与其他静态资源一起存储在 CDN 上。
¥You can use prerender functions on pages that are otherwise dynamic, allowing for partial prerendering of your data. This results in very fast navigation, since prerendered data can live on a CDN along with your other static assets.
在浏览器中,预渲染的数据使用 Cache API 保存。此缓存在页面重新加载后仍然存在,并在用户首次访问应用的新部署时被清除。
¥In the browser, prerendered data is saved using the Cache API. This cache survives page reloads, and will be cleared when the user first visits a new deployment of your app.
当整个页面都包含
export const prerender = true时,你无法使用查询,因为它们是动态的。
预渲染参数(Prerender arguments)
¥Prerender arguments
与查询一样,预渲染函数可以接受参数,该参数应为 validated 和 标准架构:
¥As with queries, prerender functions can accept an argument, which should be validated with a Standard Schema:
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error } from '@sveltejs/kit';
import { function prerender<Output>(fn: () => MaybePromise<Output>, options?: {
inputs?: RemotePrerenderInputsGenerator<void>;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<void, Output> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender } from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database';
export const const getPosts: RemotePrerenderFunction<void, void>getPosts = prerender<void>(fn: () => MaybePromise<void>, options?: {
inputs?: RemotePrerenderInputsGenerator<void>;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(async () => { /* ... */ });
export const const getPost: RemotePrerenderFunction<string, any>getPost = prerender<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any, options?: {
inputs?: RemotePrerenderInputsGenerator<string> | undefined;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(), async (slug: stringslug) => {
const [const post: anypost] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql`
SELECT * FROM post
WHERE slug = ${slug: stringslug}
`;
if (!const post: anypost) function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(404, 'Not found');
return const post: anypost;
});SvelteKit 爬虫在 预渲染页面 期间发现的任何对 getPost(...) 的调用都将自动保存,但你也可以使用 inputs 选项指定调用时应使用的值:
¥Any calls to getPost(...) found by SvelteKit’s crawler while prerendering pages will be saved automatically, but you can also specify which values it should be called with using the inputs option:
export const const getPost: RemotePrerenderFunction<string, void>getPost = prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
inputs?: RemotePrerenderInputsGenerator<string> | undefined;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(
import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(),
async (slug: stringslug) => { /* ... */ },
{
inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [
'first-post',
'second-post',
'third-post'
]
}
);export const const getPost: RemotePrerenderFunction<string, void>getPost = prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
inputs?: RemotePrerenderInputsGenerator<string> | undefined;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(
import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(),
async (slug: stringslug) => { /* ... */ },
{
inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [
'first-post',
'second-post',
'third-post'
]
}
);Svelte 尚不支持异步服务器端渲染,因此你很可能只是从浏览器调用远程函数,而不是在预渲染期间调用。因此,你目前需要使用
inputs。我们正在积极解决这个问题。
默认情况下,预渲染函数不包含在服务器包中,这意味着你无法使用任何未预渲染的参数调用它们。你可以设置 dynamic: true 来更改此行为:
¥By default, prerender functions are excluded from your server bundle, which means that you cannot call them with any arguments that were not prerendered. You can set dynamic: true to change this behaviour:
export const const getPost: RemotePrerenderFunction<string, void>getPost = prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
inputs?: RemotePrerenderInputsGenerator<string> | undefined;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(
import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(),
async (slug: stringslug) => { /* ... */ },
{
dynamic?: boolean | undefineddynamic: true,
inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [
'first-post',
'second-post',
'third-post'
]
}
);export const const getPost: RemotePrerenderFunction<string, void>getPost = prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
inputs?: RemotePrerenderInputsGenerator<string> | undefined;
dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)
Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
prerender(
import vv.function string(): v.StringSchema<undefined> (+1 overload)
export string
Creates a string schema.
string(),
async (slug: stringslug) => { /* ... */ },
{
dynamic?: boolean | undefineddynamic: true,
inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [
'first-post',
'second-post',
'third-post'
]
}
);处理验证错误(Handling validation errors)
¥Handling validation errors
只要你没有向远程函数传递无效数据,传递给 command、query 或 prerender 函数的参数验证失败只有两个原因:
¥As long as you’re not passing invalid data to your remote functions, there are only two reasons why the argument passed to a command, query or prerender function would fail validation:
函数签名在部署期间发生了变化,并且一些用户目前使用的是旧版本的应用
¥the function signature changed between deployments, and some users are currently on an older version of your app
有人正试图通过使用恶意数据攻击你暴露的端点来攻击你的网站
¥someone is trying to attack your site by poking your exposed endpoints with bad data
在第二种情况下,我们不想给攻击者任何帮助,因此 SvelteKit 将生成通用的 400 错误请求 响应。你可以通过实现 handleValidationError 服务器钩子来控制消息,该钩子与 handleError 类似,必须返回 App.Error(默认为 { message: string }):
¥In the second case, we don’t want to give the attacker any help, so SvelteKit will generate a generic 400 Bad Request response. You can control the message by implementing the handleValidationError server hook, which, like handleError, must return an App.Error (which defaults to { message: string }):
/** @type {import('@sveltejs/kit').HandleValidationError} */
export function function handleValidationError({ event, issues }: {
event: any;
issues: any;
}): {
message: string;
}
handleValidationError({ event: anyevent, issues: anyissues }) {
return {
message: stringmessage: 'Nice try, hacker!'
};
}import type { type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
issues: Issue[];
event: RequestEvent;
}) => MaybePromise<App.Error>
The handleValidationError hook runs when the argument to a remote function fails validation.
It will be called with the validation issues and the event, and must return an object shape that matches App.Error.
HandleValidationError } from '@sveltejs/kit';
export const const handleValidationError: HandleValidationErrorhandleValidationError: type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
issues: Issue[];
event: RequestEvent;
}) => MaybePromise<App.Error>
The handleValidationError hook runs when the argument to a remote function fails validation.
It will be called with the validation issues and the event, and must return an object shape that matches App.Error.
HandleValidationError = ({ event: RequestEvent<Record<string, string>, string | null>event, issues: StandardSchemaV1.Issue[]issues }) => {
return {
App.Error.message: stringmessage: 'Nice try, hacker!'
};
};如果你知道自己在做什么,并且想要退出验证,可以传递字符串 'unchecked' 代替 schema:
¥If you know what you’re doing and want to opt out of validation, you can pass the string 'unchecked' in place of a schema:
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
export const const getStuff: RemoteQueryFunction<{
id: string;
}, void>
getStuff = query<{
id: string;
}, void>(validate: "unchecked", fn: (arg: {
id: string;
}) => MaybePromise<void>): RemoteQueryFunction<{
id: string;
}, void> (+2 overloads)
Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query('unchecked', async ({ id: stringid }: { id: stringid: string }) => {
// the shape might not actually be what TypeScript thinks
// since bad actors might call this function with other arguments
});使用 getRequestEvent(Using getRequestEvent)
¥Using getRequestEvent
在 query、form 和 command 函数中,你可以使用 getRequestEvent 函数获取当前的 RequestEvent 对象。这使得构建与 Cookie 交互的抽象变得容易,例如:
¥Inside query, form and command you can use getRequestEvent to get the current RequestEvent object. This makes it easy to build abstractions for interacting with cookies, for example:
import { function getRequestEvent(): RequestEventReturns the current RequestEvent. Can be used inside server hooks, server load functions, actions, and endpoints (and functions called by them).
In environments without AsyncLocalStorage, this must be called synchronously (i.e. not after an await).
getRequestEvent, function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query } from '$app/server';
import { import findUserfindUser } from '$lib/server/database';
export const const getProfile: RemoteQueryFunction<void, {
name: any;
avatar: any;
}>
getProfile = query<{
name: any;
avatar: any;
}>(fn: () => MaybePromise<{
name: any;
avatar: any;
}>): RemoteQueryFunction<void, {
name: any;
avatar: any;
}> (+2 overloads)
Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => {
const const user: anyuser = await const getUser: (arg: void) => RemoteQuery<any>getUser();
return {
name: anyname: const user: anyuser.name,
avatar: anyavatar: const user: anyuser.avatar
};
});
// this query could be called from multiple places, but
// the function will only run once per request
const const getUser: RemoteQueryFunction<void, any>getUser = query<any>(fn: () => any): RemoteQueryFunction<void, any> (+2 overloads)Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
See Remote functions for full documentation.
query(async () => {
const { const cookies: CookiesGet or set cookies related to the current request
cookies } = function getRequestEvent(): RequestEventReturns the current RequestEvent. Can be used inside server hooks, server load functions, actions, and endpoints (and functions called by them).
In environments without AsyncLocalStorage, this must be called synchronously (i.e. not after an await).
getRequestEvent();
return await import findUserfindUser(const cookies: CookiesGet or set cookies related to the current request
cookies.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefinedGets a cookie that was previously set with cookies.set, or from the request headers.
get('session_id'));
});请注意,RequestEvent 的某些属性在远程函数内部有所不同:
¥Note that some properties of RequestEvent are different inside remote functions:
你无法设置 header(除了写入 cookie,并且只能在
form和command函数内设置)¥you cannot set headers (other than writing cookies, and then only inside
formandcommandfunctions)route、params和url与调用远程函数的页面相关,而不是 SvelteKit 为远程函数创建的端点的 URL。用户导航时,查询不会重新运行(除非查询的参数因导航而发生变化),因此你应该注意如何使用这些值。尤其切勿使用它们来确定用户是否有权访问某些数据。¥
route,paramsandurlrelate to the page the remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Queries are not re-run when the user navigates (unless the argument to the query changes as a result of navigation), and so you should be mindful of how you use these values. In particular, never use them to determine whether or not a user is authorized to access certain data.
重定向(Redirects)
¥Redirects
在 query、form 和 prerender 函数中,可以使用 redirect(...) 函数。在 command 函数内部无法实现此功能,因为你应该避免在此处重定向。(如果确实需要,你可以返回一个 { redirect: location } 对象并在客户端中处理它。)
¥Inside query, form and prerender functions it is possible to use the redirect(...) function. It is not possible inside command functions, as you should avoid redirecting here. (If you absolutely have to, you can return a { redirect: location } object and deal with it in the client.)