您可能习惯于构建不在服务器上运行的 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 将执行对服务器的 fetch,以从 loader
获取数据并渲染路由。加载数据到组件的整个任务都已完成。您不需要 API 路由来满足路由组件的数据需求,它们已经是它们自己的 API。
但是,有时您希望从加载器获取数据,但不是因为用户正在访问路由,而是因为当前页面出于某种原因需要该路由的数据。一个非常明显的例子是一个 <Combobox>
组件,它查询数据库以获取记录并向用户建议它们。
对于这种情况,您可以使用 useFetcher
。而且,由于浏览器中的 Remix 了解服务器上的 Remix,因此您无需费心获取数据。Remix 的错误处理会介入,并且也会为您处理竞态条件、中断和 fetch 取消。
例如,您可以有一条路由来处理搜索
export async function loader({
request,
}: LoaderFunctionArgs) {
const url = new URL(request.url);
return json(
await searchCities(url.searchParams.get("q"))
);
}
然后使用 Reach UI 的下拉菜单输入使用 useFetcher
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
调用,则返回操作的响应。
以下是一些用例,可以帮助您进行思考。
您可以在资源路由文档中了解更多信息。