Remix defer()基础示例不符合预期:等待所有Promise完成才传输数据
问题原因与解决方案
你的代码逻辑本身是正确的,但页面会等待3秒后同时渲染两个内容,核心原因是服务器端渲染未启用流式传输,导致Remix必须等待所有deferred数据加载完成后,才会发送完整的HTML页面到浏览器。
解决步骤
1. 替换entry.server.tsx为流式渲染实现
Remix默认的非流式渲染(如renderToString)会阻塞页面输出,直到所有Suspense包裹的异步数据加载完成。你需要确保entry.server.tsx使用React的流式渲染API(renderToPipeableStream或renderToReadableStream)。
替换为以下代码:
import { PassThrough } from "node:stream"; import type { AppLoadContext, EntryContext } from "@remix-run/node"; import { createReadableStreamFromReadable } from "@remix-run/node"; import { RemixServer } from "@remix-run/react"; import { isbot } from "isbot"; import { renderToPipeableStream } from "react-dom/server"; const ABORT_DELAY = 5_000; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, loadContext: AppLoadContext ) { return isbot(request.headers.get("user-agent") || "") ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); } function handleBotRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { return new Promise((resolve, reject) => { let shellRendered = false; const { pipe, abort } = renderToPipeableStream( <RemixServer context={remixContext} url={request.url} />, { onAllReady() { shellRendered = true; const body = new PassThrough(); const stream = createReadableStreamFromReadable(body); responseHeaders.set("Content-Type", "text/html"); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, }) ); pipe(body); }, onShellError(error: unknown) { reject(error); }, onError(error: unknown) { responseStatusCode = 500; if (shellRendered) console.error(error); }, } ); setTimeout(abort, ABORT_DELAY); }); } function handleBrowserRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { return new Promise((resolve, reject) => { let shellRendered = false; const { pipe, abort } = renderToPipeableStream( <RemixServer context={remixContext} url={request.url} />, { onShellReady() { shellRendered = true; const body = new PassThrough(); const stream = createReadableStreamFromReadable(body); responseHeaders.set("Content-Type", "text/html"); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, }) ); pipe(body); }, onShellError(error: unknown) { reject(error); }, onError(error: unknown) { responseStatusCode = 500; if (shellRendered) console.error(error); }, } ); setTimeout(abort, ABORT_DELAY); }); }
2. 确认依赖版本与服务器支持
- 确保使用Remix v1.15+或v2+版本,这些版本原生支持
defer的流式传输。 - 确保你的服务器(如Remix默认Express服务器、Vercel、Netlify等)支持HTTP流式响应,自定义服务器需手动实现流式处理逻辑。
效果验证
修改后,页面会在请求发起约1秒后渲染quickAndCritical,再过2秒渲染slow,完全符合你的预期。
内容的提问来源于stack exchange,提问作者felixmp
相关产品推荐
相关产品推荐

