API 路由

API 路由

您可能习惯于构建不在服务器上运行的 React 应用程序,或者至少它的大部分不在服务器上运行,因此它由一组 API 路由支持。在 Remix 中,您的大多数路由既是您的 UI,也是您的 API,因此浏览器中的 Remix 了解如何与服务器上的自身进行通信。

通常,您根本不需要“API 路由”的概念。但我们知道您会带着这个术语来探查,所以我们来了!

路由是它们自己的 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 调用,则返回加载器的响应。如果使用 POSTPUTPATCHDELETE 调用,则返回操作的响应。

以下是一些用例,可以帮助您进行思考。

  • 用于移动应用程序的 JSON API,该 API 与 Remix UI 重用服务器端代码
  • 动态生成 PDF
  • 为博客文章或其他页面动态生成社交图片
  • 其他服务的 Webhook

您可以在资源路由文档中了解更多信息。

文档和示例根据 MIT