API 路由

API 路由

¥API Routes

你可能习惯于构建不在服务器上运行的 React 应用,或者至少很少在服务器上运行,因此它由一组 API 路由支持。在 Remix 中,大多数路由既是 UI,又是 API,因此浏览器中的 Remix 知道如何与服务器上的自身通信。

¥You might be used to building React apps that don't run on the server, or at least not very much of it does, so it's backed by a set of API routes. In Remix, most of your routes are both your UI and your API, so Remix in the browser knows how to talk to itself on the server.

通常,你根本不需要 "API 路由" 的概念。但我们知道你会来探索这个术语,所以我们来了!

¥In general, you don't need the concept of "API Routes" at all. But we knew you'd come poking around with this term, so here we are!

路由有自己的 API

¥Routes Are Their Own API

考虑此路由:

¥Consider this route:

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。

¥Whenever the user clicks a link to <Link to="/teams" />, Remix in the browser will perform the fetch to the server to get the data from the loader and render the route. The entire task of loading data into components has been taken care of. You don't need API routes for data requirements of your route components, they are already their own API.

在导航之外调用加载器

¥Call Loaders Outside of Navigation

但是,有时你希望从加载器获取数据,但不是因为用户正在访问该路由,而是因为当前页面出于某种原因需要该路由的数据。一个非常清晰的例子是 <Combobox> 组件,它查询数据库并向用户推荐记录。

¥There are times, however, that you want to get the data from a loader but not because the user is visiting the route, but the current page needs that route's data for some reason. A very clear example is a <Combobox> component that queries the database for records and suggests them to the user.

你可以针对以下情况使用 useFetcher。再次强调,由于浏览器中的 Remix 知道服务器上存在 Remix,因此你无需执行太多操作即可获取数据。Remix 的错误处理功能将启动,并且还会为你处理竞争条件、中断和获取取消。

¥You can useFetcher for cases like this. And once again, since Remix in the browser knows about Remix on the server, you don't have to do much to get the data. Remix's error handling kicks in, and race conditions, interruptions, and fetch cancellations are handled for you, too.

例如,你可以有一个路由来处理搜索:

¥For example, you could have a route to handle the search:

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const url = new URL(request.url);
  return json(
    await searchCities(url.searchParams.get("q"))
  );
}

然后是 useFetcher 以及 Reach UI 的组合框输入:

¥And then useFetcher along with Reach UI's combobox input:

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>
  );
}

资源路由

¥Resource Routes

在其他情况下,你可能需要一些属于应用一部分但不属于应用 UI 的路由。也许你想要一个将报告渲染为 PDF 的加载器:

¥In other cases, you may need routes that are part of your application but aren't part of your application's UI. Maybe you want a loader that renders a report as a 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 调用,则会返回操作的响应。

¥If a route is not called by Remix UI (like <Link> or useFetcher), and does not export a default component, it is now a general purpose Resource Route. If called with GET, the loader's response is returned. If called with POST, PUT, PATCH, or DELETE, the action's response is returned.

以下是一些用例,供你参考。

¥Here are a handful of use cases to get you thinking.

  • 适用于移动应用的 JSON API,该应用使用 Remix UI 复用服务器端代码。

    ¥JSON API for a mobile app that reuses server-side code with the Remix UI

  • 动态生成 PDF

    ¥Dynamically generating PDFs

  • 为博客文章或其他页面动态生成社交图片

    ¥Dynamically generating social images for blog posts or other pages

  • 用于其他服务的 Webhook

    ¥Webhooks for other services

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

¥You can read more in the Resource Routes docs.

Remix v2.17 中文网 - 粤ICP备13048890号