React Router v7 已发布。 查看文档
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 将执行获取操作以从服务器获取 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 调用,则返回加载器的响应。如果使用 POSTPUTPATCHDELETE 调用,则返回操作的响应。

以下是一些用例,可以让你思考。

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

你可以在资源路由文档中阅读更多内容。

文档和示例根据以下许可: MIT