entry.server.tsx
本页内容

entry.server.tsx

摘要

此文件是可选的

该文件是服务器端的入口点,控制 React Router 应用程序如何在服务器上生成 HTTP 响应。

该模块应使用带有当前请求的 `context` 和 `url` 的 <ServerRouter> 元素来呈现当前页面的标记。一旦 JavaScript 在浏览器中加载,该标记将(可选地)使用 客户端入口模块 进行再水合(re-hydrated)。

生成 entry.server.tsx

默认情况下,React Router 将为您处理 HTTP 响应的生成。您可以通过以下方式显示默认的入口服务器文件:

npx react-router reveal

导出

default

该模块的 `default` 导出是一个函数,它允许您创建响应,包括 HTTP 状态、头信息和 HTML,让您完全控制标记的生成和发送到客户端的方式。

import { PassThrough } from "node:stream";
import type { EntryContext } from "react-router";
import { createReadableStreamFromReadable } from "@react-router/node";
import { ServerRouter } from "react-router";
import { renderToPipeableStream } from "react-dom/server";

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  routerContext: EntryContext
) {
  return new Promise((resolve, reject) => {
    const { pipe, abort } = renderToPipeableStream(
      <ServerRouter
        context={routerContext}
        url={request.url}
      />,
      {
        onShellReady() {
          responseHeaders.set("Content-Type", "text/html");

          const body = new PassThrough();
          const stream =
            createReadableStreamFromReadable(body);

          resolve(
            new Response(stream, {
              headers: responseHeaders,
              status: responseStatusCode,
            })
          );

          pipe(body);
        },
        onShellError(error: unknown) {
          reject(error);
        },
      }
    );
  });
}

streamTimeout

如果您正在流式传输响应,您可以导出一个可选的 `streamTimeout` 值(以毫秒为单位),该值将控制服务器在拒绝未完成的 Promise 并关闭流之前等待流式 Promise 解决的时间。

建议将此值与您中止 React 渲染器的超时值分离开来。您应始终将 React 渲染超时设置为一个更高的值,以便它有时间从您的 `streamTimeout` 中流式传输底层的拒绝信息。

// Reject all pending promises from handler functions after 10 seconds
export const streamTimeout = 10000;

export default function handleRequest(...) {
  return new Promise((resolve, reject) => {
    // ...

    const { pipe, abort } = renderToPipeableStream(
      <ServerRouter context={routerContext} url={request.url} />,
      { /* ... */ }
    );

    // Abort the streaming render pass after 11 seconds to allow the rejected
    // boundaries to be flushed
    setTimeout(abort, streamTimeout + 1000);
  });
}

handleDataRequest

您可以导出一个可选的 `handleDataRequest` 函数,该函数将允许您修改数据请求的响应。这些请求不渲染 HTML,而是在客户端水合发生后将 `loader` 和 `action` 数据返回到浏览器。

export function handleDataRequest(
  response: Response,
  {
    request,
    params,
    context,
  }: LoaderFunctionArgs | ActionFunctionArgs
) {
  response.headers.set("X-Custom-Header", "value");
  return response;
}

handleError

默认情况下,React Router 会将遇到的服务器端错误记录到控制台。如果您想更多地控制日志记录,或者希望将这些错误报告给外部服务,那么您可以导出一个可选的 `handleError` 函数,该函数将为您提供控制权(并将禁用内置的错误日志记录)。

export function handleError(
  error: unknown,
  {
    request,
    params,
    context,
  }: LoaderFunctionArgs | ActionFunctionArgs
) {
  if (!request.signal.aborted) {
    sendErrorToErrorReportingService(error);
    console.error(formatErrorForJsonLogging(error));
  }
}

请注意,当请求被中止时,您通常希望避免日志记录,因为 React Router 的取消和竞争条件处理可能会导致大量请求被中止。

流式渲染错误

当您通过 renderToPipeableStreamrenderToReadableStream 流式传输 HTML 响应时,您自己的 `handleError` 实现将只处理初始 shell 渲染期间遇到的错误。如果在后续的流式渲染期间遇到渲染错误,您将需要手动处理这些错误,因为 React Router 服务器在那时已经发送了响应。

对于 renderToPipeableStream,您可以在 onError 回调函数中处理这些错误。您需要在 onShellReady 中切换一个布尔值,以便知道错误是 shell 渲染错误(可以忽略)还是异步错误。

有关示例,请参阅 Node 的默认 entry.server.tsx 文件。

抛出的响应

请注意,这不会处理从您的 `loader`/`action` 函数中抛出的 `Response` 实例。此处理程序的目的是查找导致意外抛出错误的代码中的 bug。如果您检测到某种情况并在您的 `loader`/`action` 中抛出一个 401/404/等 `Response`,那么这是一个由您的代码处理的预期流程。如果您也希望记录或将这些发送到外部服务,那应该在您抛出响应时完成。

文档和示例 CC 4.0
编辑