前端开发
用 React Router…
在 React Router 应用中编写 loader、action、表单、路由组织、流式渲染与 SEO 时,按此清单落地性能与架构规范。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
用 React Router 写路由/数通用
解决思路
- 所有数据获取都放在 loader 中,绝不在组件里用 useEffect 拉取;组件通过 useLoaderData<typeof loader>() 读取数据。
- loader 内并行取数:先取依赖数据(如 user),再用 Promise.all 并行取 posts、comments,避免串行 await 造成瀑布。
- 利用请求级缓存:同一请求内 API 客户端会去重,多个 loader 可各自调用 getUser,不会产生额外请求。
- 用 useRevalidator 做轮询/聚焦/重连时的重新校验;轮询前判断 document.visibilityState === 'hidden' 则跳过,避免后台标签页轮询。
- 用 Route.LoaderArgs 做 TypeScript 类型标注,配合 useLoaderData<typeof loader>() 获得类型推断。
- 在 loader 里用 zod 或 invariant 尽早校验 URL 参数(如 z.string().parse(params.itemId))。
- 把 request.signal 传给 fetch,请求取消时中止异步工作。
- 把数据查询函数放在路由同目录的 queries.server.ts 中,route.tsx 只调用这些查询函数,组件放 components/ 子目录。
- 用 middleware 做认证(sessionMiddleware、authMiddleware),在每个 loader/action 中调用 authorize(context, {...}) 做授权。
- 每个请求保持单一 session 实例(sessionMiddleware)。
- 用 AsyncLocalStorage 存 context/request,便于无参 helper 使用(contextStorageMiddleware)。
- 用 batcher 去重请求级 API/DB 调用:getBatcher().batch('key', () => getData())。
- 为请求加 request ID 便于日志关联:getRequestID()。
- 用内置 loggerMiddleware 统一记录请求日志。
- 用 Server-Timing 记录耗时:getTimingCollector().measure('load', 'Load data', () => getData())。
- 为缓存创建请求级单例:getSingleton(context)。
- 用 Sec-Fetch 头拒绝跨站变更请求:fetchSite(request) === 'cross-site' 时返回 403。
- 公开表单加蜜罐输入 <HoneypotInputs /> 防机器人。
- API 路由应用 CORS 头:return await cors(request, data(await getData()))。
- 对用户驱动的跳转做安全清洗:redirect(safeRedirect(redirectTo, '/'))。
- 用 schema 校验 cookie 载荷:createTypedCookie({ cookie, schema })。
- 从可信代理头提取客户端 IP:getClientIPAddress(request)。
- UI 层访问父路由数据用 useRouteLoaderData;loader 逻辑需要数据时直接再取一次(请求级缓存,无额外请求)。
- 只有路由组件调用 useLoaderData/useActionData,子组件通过 props 接收数据。
- action 中用 zod schema 校验表单数据,错误信息支持 i18n;校验失败返回 data({ errors }, { status: 400 }),不要抛异常。
- action 错误处理:校验错误返回 data,redirect 和未知错误要重新 throw。
- 变更成功后用 throw redirect('/items') 跳转,防止重复提交。
- 用 zod 的 .transform() 做输入清洗:trim/lowercase 邮箱、把货币字符串转数字、把 checkbox 转布尔。
- 用 clientAction 在提交到服务端前做即时客户端校验,校验通过再调用 serverAction。
- 非导航型变更(如点赞)用 useFetcher,需要导航的提交用 Form。
- 用 useNavigation 或 fetcher.state 显示 pending 状态;配合 useSpinDelay 避免闪烁。
- 提交成功后重置非受控表单:在 fetcher.state === 'idle' && fetcher.data?.ok 时调用 formRef.current?.reset()。
- 校验失败时 action 返回字段值(fields),组件用 defaultValue={actionData?.fields?.email} 回填。
- 用 clientLoader 在路由层做防抖:setTimeout(500, serverLoader, { signal: request.signal }),并设置 clientLoader.hydrate = true。
- 把旧的 defer() 迁移为 data() + Promise(Single Fetch 自动流式)。
- 流式数据用 <Suspense> 包裹 <Await resolve={lazy}> 渲染。
- 弃用 jsonHash,改用原生 Promise.all 或 data() 配合 Promise 自动流式。
- 把已废弃的 json() 迁移为 data(),包括带状态码和 throw data(...) 的用法。
- 把 namedAction 迁移为 z.discriminatedUnion('intent', [...]) 做类型安全的意图校验。
- 用 useRouteError 和 isRouteErrorResponse 实现布局感知的 ErrorBoundary。
- 为有数据获取的路由添加 ErrorBoundary 以捕获 loader/action 错误。
- 导航链接用 prefetch="intent" 在 hover/focus 时预取。
- 应用内返回链接避免 navigate(-1),改用 <Link to={...} state={{ back: location.pathname }}>。
- 用 PrefetchPageLinks 预加载 fetcher.load() 所需数据。
- 资源路由用响应 helper,如 html('<h1>Hello</h1>')。
- 用 eventStream 和 useEventSource 推送 SSE 更新。
- 预取请求使用短缓存:isPrefetch(request) 时设置 Cache-Control: private, max-age=5。
- 路由用文件夹组织:queries.server.ts、actions.server.ts、route.tsx、components/ 同目录。
- API 类端点用资源路由(无 default export 的路由文件)。
- 把可复用 action 集中到 actions.noun-verb.ts 资源路由,并配 clientAction 做 toast 与跳转。
- 用 shouldRevalidate 优化重新校验,例如仅 hash 变化时返回 false。
- 用 handle 导出配合应用定义的 handle 类型控制路由元数据(hydrate、stickyHeader、footerType)。
- 用 meta 函数配合 loader 数据做动态 SEO,返回 title、description、og:* 等;也可在 loader 中集中生成 meta 再返回。
- 路由文件的默认导出统一命名为 Component。
- 路由文件之间不要互相 import;共享组件放 ~/components 等共享位置,仅可 import 其他路由的 loader/action 类型用于 useFetcher 类型推断。
适用边界 · 注意事项
- 不使用 React Router 的项目不适用。
- 未使用 TypeScript 或 Route 类型生成的项目,部分类型标注写法需调整。
- 不涉及服务端渲染/数据路由(纯客户端 SPA 无 loader/action)时,多数规则不适用。