你可能习惯于构建不在服务器上运行的 React 应用,或者至少大部分不在服务器上运行,因此它由一组 API 路由支持。在 Remix 中,你的大多数路由既是你的 UI 又是你的 API,因此浏览器中的 Remix 知道如何在服务器上与自身通信。
一般来说,你根本不需要“API 路由”的概念。但我们知道你会带着这个术语来探索,所以我们在这里!
考虑这个路由
export async function loader() {
return json(await getTeams());
}
export default function Teams() {
return (
<TeamsView teams={useLoaderData<typeof loader>()} />
);
}
每当用户点击指向 <Link to="/teams" />
的链接时,浏览器中的 Remix 将执行获取操作以从服务器获取 loader
中的数据并渲染该路由。将数据加载到组件的整个任务已经完成。你的路由组件的数据需求不需要 API 路由,它们已经是它们自己的 API。
但是,有时你希望从加载器获取数据,但这并不是因为用户正在访问该路由,而是当前页面出于某种原因需要该路由的数据。一个非常清晰的示例是一个 <Combobox>
组件,它查询数据库以查找记录并将其建议给用户。
你可以为这种情况使用 useFetcher
。再次强调,由于浏览器中的 Remix 知道服务器上的 Remix,因此你无需执行太多操作即可获取数据。Remix 的错误处理会启动,并且还会为你处理竞争条件、中断和获取取消。
例如,你可以有一个路由来处理搜索
export async function loader({
request,
}: LoaderFunctionArgs) {
const url = new URL(request.url);
return json(
await searchCities(url.searchParams.get("q"))
);
}
然后将 useFetcher
与 Reach UI 的组合框输入一起使用
function CitySearchCombobox() {
const cities = useFetcher();
return (
<cities.Form method="get" action="/city-search">
<Combobox aria-label="Cities">
<div>
<ComboboxInput
name="q"
onChange={(event) =>
cities.submit(event.target.form)
}
/>
{cities.state === "submitting" ? (
<Spinner />
) : null}
</div>
{cities.data ? (
<ComboboxPopover className="shadow-popup">
{cities.data.error ? (
<p>Failed to load cities :(</p>
) : cities.data.length ? (
<ComboboxList>
{cities.data.map((city) => (
<ComboboxOption
key={city.id}
value={city.name}
/>
))}
</ComboboxList>
) : (
<span>No results found</span>
)}
</ComboboxPopover>
) : null}
</Combobox>
</cities.Form>
);
}
在其他情况下,你可能需要作为应用程序一部分,但不是应用程序 UI 一部分的路由。也许你想要一个将报告渲染为 PDF 的加载器
export async function loader({
params,
}: LoaderFunctionArgs) {
const report = await getReport(params.id);
const pdf = await generateReportPDF(report);
return new Response(pdf, {
status: 200,
headers: {
"Content-Type": "application/pdf",
},
});
}
如果一个路由不是由 Remix UI(如 <Link>
或 useFetcher
)调用,并且不导出默认组件,那么它现在是一个通用的资源路由。如果使用 GET
调用,则返回加载器的响应。如果使用 POST
、PUT
、PATCH
或 DELETE
调用,则返回操作的响应。
以下是一些用例,可以让你思考。
你可以在资源路由文档中阅读更多内容。