服务端函数
用 server$ 把任意函数变成类型安全的 RPC,同时保留 RequestEvent。
本页目录3 节
server$() 把一个函数变成客户端可调用的 RPC。调用方拿到的仍是普通异步函数,但函数体只在服务器上执行,参数与返回值经过 Qwik 序列化。
import { component$, useSignal } from '@qwik.dev/core';
import { server$ } from '@qwik.dev/router';
export const sendMessage = server$(async function (name: string) {
await mailer.send({
to: 'hello@example.com',
subject: `来自 ${name} 的消息`,
});
return { ok: true };
});
export default component$(() => {
const name = useSignal('');
const status = useSignal('');
return (
<form
preventdefault:submit
onSubmit$={async () => {
const res = await sendMessage(name.value);
status.value = res.ok ? '已发送' : '失败';
}}
>
<input bind:value={name} />
<button type="submit">发送</button>
<p>{status.value}</p>
</form>
);
});参数可以是原始值、对象、数组、Date、Map、Set、Promise 以及可序列化的 QRL。不能传类实例、函数或流;需要传 DOM 引用时用 noSerialize() 明确标注。
01在服务端拿到请求上下文#
server$ 函数中的 this 是 RequestEvent。你可以读取 Cookie、Header、URL 与环境变量,也可以写响应 Cookie。
export const whoami = server$(function () {
const session = this.cookie.get('session')?.value;
const token = this.env.get('API_TOKEN');
return {
session: session ?? null,
authorized: Boolean(session && token),
};
});02可取消的调用#
当调用方传入 AbortSignal 时,server$ 函数会把它作为第一个参数注入。连接断开后你可以提前终止长任务。
export const search = server$(async function (
signal: AbortSignal,
query: string
) {
const res = await fetch(
`https://api.example.com/search?q=${encodeURIComponent(query)}`,
{ signal }
);
return res.json();
});
// 调用
const controller = new AbortController();
const result = await search(controller.signal, 'qwik');03什么该放在服务端#
| 需求 | 首选 |
|---|---|
| 页面渲染前需要数据 | routeLoader$ |
| 表单写操作 | routeAction$ |
| 按钮触发的任意服务端逻辑 | server$ |
| 对外提供 REST / Webhook | index.ts 端点 |
| 数据库、密钥、第三方 SDK | .server.ts 模块 |
文件名包含 .server. 或位于 src/**/server/** 的模块只允许服务端代码导入。客户端若误引,构建会直接失败,而不是把密钥悄悄打进 bundle。
export async function findUser(id: string) {
return db.users.find(id);
}