如何把 React Router 构建产物接入 Express 服务器并桥接服务端上下文?
发布时间:2026/9/9 13:56:24来源:尧图网络
如何把 React Router 构建产物接入 Express 服务器并桥接服务端上下文【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router如果你用的是 React Router 的 framework modereact-router build会产出构建文件但又不想依赖内置的 React Router App Serverreact-router/serve而是想让 Express 完全接管请求——比如挂上 Express 自己的中间件、session 或静态资源策略——就需要官方适配器react-router/express。它做两件事把 Express 的req/res转成 React Router 能处理的请求并通过getLoadContext把服务端Express 侧的值桥接进路由的loader/action中。本文按仓库文档和仓库内 Express 示例应用 整理这条路径安装依赖 → 编写服务器入口 → 配置 Vite 编译服务器代码 → 更新脚本 → 用 context 桥接服务端数据 → 启动验证。Node 版本方面适配器文档 说明 React Router 官方支持所有Active LTS版本以及Maintenance LTS的最新 minor 线文档以 2026-06-17 为例Node 24 为 Active LTS全部24.x受支持Node 22 为 Maintenance LTS支持22.22.x。仓库内 Express 示例声明的最低版本是node 22.22.0见 playground/framework-express/package.json。1. 安装依赖如果应用已经用react-router/serve创建迁移时先卸载它再安装 Express 栈docs/api/other-api/adapter.md 给出的依赖更新命令npm uninstall react-router/serve npm install react-router/express compression express morgan cross-env npm install --save-dev types/express types/express-serve-static-core types/morgan全新项目只需按 packages/react-router-express/README.md 安装适配器npm install react-router/express2. 编写 Express 服务器入口核心是react-router/express导出的createRequestHandler见 packages/react-router-express/index.ts它接收两个关键配置buildReact Router 的服务端构建产物。react-router build和react-router dev会把文件输出到 build 目录这个参数就是把它传给请求处理器getLoadContext(req, res)返回的值会成为loader和action里的context这是“服务端 ↔ React Router”的桥。源码注释明确说它是把 Express 中间件产生的值例如req.session传进 loader/action 的 escape hatch见 packages/react-router-express/server.ts。开发用服务器TypeScript 版本适配器文档 的迁移指南建议在server/app.ts中这样写// server/app.ts import react-router; import { createRequestHandler } from react-router/express; import express from express; export const app express(); app.use( createRequestHandler({ build: () import(virtual:react-router/server-build), }), );virtual:react-router/server-build是 React Router Vite 插件提供的虚拟模块开发时由 Vite 解析。文档同时建议从 Express 模板复制server.js作为样板用两个文件分开是因为主 Express 服务器代码写在 TypeScript 里server/app.ts由 React Router 编译进服务端构建最终通过node server.js执行使同一套服务器代码同时运行开发和生产构建。仓库内的完整示例开发 生产一份代码仓库自带一个可直接参照的示例 playground/framework-express/server.js它按NODE_ENV区分两种构建来源开发时用 Vite dev server 的ssrLoadModule(virtual:react-router/server-build)生产时加载./build/server/index.js// playground/framework-express/server.js import { createRequestHandler } from react-router/express; import compression from compression; import express from express; import morgan from morgan; const viteDevServer process.env.NODE_ENV production ? undefined : await import(vite).then((vite) vite.createServer({ server: { middlewareMode: true }, }), ); const reactRouterHandler createRequestHandler({ build: viteDevServer ? () viteDevServer.ssrLoadModule(virtual:react-router/server-build) : await import(./build/server/index.js), }); const app express(); app.use(compression()); app.disable(x-powered-by); if (viteDevServer) { app.use(viteDevServer.middlewares); } else { app.use( /assets, express.static(build/client/assets, { immutable: true, maxAge: 1y }), ); } app.use(express.static(build/client, { maxAge: 1h })); app.use(morgan(tiny)); app.all(*, reactRouterHandler); const port process.env.PORT || 3000; app.listen(port, () console.log(Express server listening at http://localhost:${port}), );注意最后一条app.all(*, reactRouterHandler)文档强调 handler 必须处理所有 HTTP 动词GET、POST 等React Router 的 action表单提交走 POST只注册 GET 会导致 action 不生效。这个示例还体现了构建产物的目录约定客户端产物在build/client静态托管服务端产物入口是build/server/index.js。如果你不走 Vite 编译服务器的路线createRequestHandler也可以直接指向构建目录适配器文档 给出的最简生产形态是const { createRequestHandler } require(react-router/express); const express require(express); const app express(); // needs to handle all verbs (GET, POST, etc.) app.all( *, createRequestHandler({ // react-router build 和 react-router dev 输出文件到 build 目录 // 需要把该构建产物传给 request handler build: require(./build), // 这里返回的任何值都会在 loader 和 action 中作为 context 可用 // 这是桥接你的 Express 服务器与 React Router 的位置 getLoadContext(req, res) { return {}; }, }), );3. 让 Vite 编译服务器代码走server/app.ts路线时需要修改vite.config.ts让 SSR 构建以server/app.ts为入口docs/api/other-api/adapter.md// vite.config.ts import { reactRouter } from react-router/dev/vite; import { defineConfig } from vite; import tsconfigPaths from vite-tsconfig-paths; export default defineConfig(({ isSsrBuild }) ({ build: { rollupOptions: isSsrBuild ? { input: ./server/app.ts } : undefined, }, plugins: [reactRouter(), tsconfigPaths()], }));仓库内示例 playground/framework-express/vite.config.ts 是更简单的版本服务器代码不经 SSR 构建编译直接由server.js运行时加载两种写法分别对应上面第 2 节的两种服务器入口。4. 更新 package.json 脚本适配器文档 要求把dev和start脚本改为使用新的 Express 服务器{ scripts: { dev: cross-env NODE_ENVdevelopment node --conditions development server.js, start: node server.js } }其中--conditions development必须保留在dev脚本里文档说明其作用是让开发时使用正确版本的 React Router。仓库内示例 playground/framework-express/package.json 用的是等价变体dev: node ./server.js服务器内部用 Vite dev server 处理开发态start: cross-env NODE_ENVproduction node ./server.js构建脚本为build: react-router build。5. 桥接服务端上下文到 loader/actiongetLoadContext返回普通对象即可被context读取如果要类型安全React Router 提供了createContextRouterContextProvider组合docs/api/utils/createContext.md、docs/api/utils/RouterContextProvider.md// app/context.ts import { createContext } from react-router; export const userContext createContextUser | null(null);在 Express 侧用RouterContextProvider写入docs/api/utils/RouterContextProvider.mdimport { createContext, RouterContextProvider } from react-router; const contextProvider new RouterContextProvider(); contextProvider.set(userContext, getUserFromExpressSession(req));getLoadContext返回该 provider 后loader里就能以类型安全的方式读出// app/routes/profile.tsx import { userContext } from ~/context; export async function loader({ context }: Route.LoaderArgs) { const user context.get(userContext); if (!user) { throw new Response(Unauthorized, { status: 401 }); } return { user }; }两个要点均来自上述文档createContext提供的defaultValue会在未 set 时由context.get()返回不提供 defaultValue 时未 set 就读会抛错。Cloudflare 适配器示例还展示了同一套模式在fetch里new RouterContextProvider()并set平台上下文后再交给请求处理器见 docs/api/other-api/adapter.mdExpress 侧用法一致。6. 构建与启动验证按文档命令顺序执行react-router build # 产出 build 目录客户端 服务端 node server.js # 或按你更新后的 start 脚本仓库内示例的server.js在监听成功后会打印示例日志端口跟随PORT环境变量默认 3000Express server listening at http://localhost:3000之后在浏览器访问http://localhost:3000应能看到 React Router 应用渲染页面静态资源由示例中的express.static(build/client, ...)提供。如果 GET 页面正常但表单提交POST action不生效先检查 handler 是否用app.all(*, ...)注册了全部 HTTP 动词——这是文档明确强调的要求。限制与边界使用内置react-router/serve的应用“不接触这套 API”适配器只在自定义服务器入口里使用不写进 React Router 应用代码本身docs/api/other-api/adapter.md。请求转换细节适配器会尊重 Express 的trust proxy设置——启用时从X-Forwarded-Host取主机和端口否则用Host头并监听响应close事件在无法再写响应时中止 loader/action见 packages/react-router-express/server.ts。Express 不支持异步错误传递适配器内部在出错时手动调用next(error)交给 Express 错误处理链同上文件所以你在app上挂的 Express error handler 仍然有效。如果后续要更进一步仓库的middleware文档docs/how-to/middleware.md描述了在 React Router 请求生命周期内使用中间件消费context的完整方式可与本文的桥接路径组合使用。【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网