
1. Codex 写 React 大列表为什么数据一多就卡先定位无效渲染来源用 Codex 生成 React 列表页几十条数据时一切正常数据涨到 500、2000、10000 条后页面开始出现首次进入变慢、滚动掉帧、搜索框输入卡顿、勾选一行几千行一起重渲染、内存持续上升。这类问题在后台管理系统、Next.js 数据看板里非常常见核心检索词就是 React 大列表卡顿与无效渲染。很多人第一反应是让 Codex 加useMemo、useCallback、React.memo但改完发现效果有限因为瓶颈根本不在函数创建而在于页面同时维护了太多实际看不到的 DOM 节点。我试过拿一个 10000 行的表格做对照每行是tr加 5 个td再加按钮、图标、文本节点浏览器要同时维护 10000 个 tr、50000 个 td以及对应的布局计算、样式计算、事件系统和 React Fiber 节点。而用户屏幕实际只能看到 20 到 30 行。也就是说超过 99% 的渲染成本花在了看不见的内容上。所以优化大列表的第一性问题不是「怎样让 10000 行渲染得更快」而是「为什么要同时渲染 10000 行」。定位无效渲染来源建议按三个维度排查。第一是渲染次数用 React DevTools Profiler 录制一次交互看哪个组件 Commit 次数最多、单次 Commit 耗时多少。第二是 DOM 节点规模在 Elements 面板搜索tr或列表项标签数一下实际节点数量如果远超可视行数说明没有虚拟化。第三是滚动性能Chrome Performance 录制滚动过程看 Long Task 是否超过 50msScripting、Rendering、Painting 各占多少。如果 Rendering 占比高通常是 DOM 和布局问题如果 Scripting 占比高通常是 JS 计算问题。两者优化方向完全不同。还有一个容易被忽略的点Codex 生成的代码里父组件状态往往放得太高。比如selectedId、hoverId、expandedId、搜索关键词全放在列表父组件任何一个变化都会让父组件重新执行进而让所有UserRow进入渲染流程。几十行无所谓几千行就是明显卡顿。所以排查时要问清楚一次状态更新到底有多少行会重新渲染这个数字才是决定卡不卡的关键。2. TaoToken 前置准备给 Codex 配好 Base URL、Key 和 Model ID要让 Codex 稳定地帮你分析和重构大列表先把接入配置做对。TaoToken 提供统一的 API 入口官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。下面这套配置三件套Base URL Key Model ID在 Claude Code、Cline、Codex 里都通用建议先复制保存。先到控制台创建 API Key入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建后立刻复制页面刷新后不再完整显示。然后在项目根目录或用户目录准备配置文件。以 Claude Code 的settings.json为例路径通常是~/.claude/settings.json内容如下{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: sk-你的TaoTokenKey, ANTHROPIC_MODEL: claude-sonnet-4-5-20250929 } }如果你用的是 Cline 或 Roo Code 这类 VS Code 插件在设置面板里填三项即可API Provider 选 Anthropic 兼容Base URL 填https://taotoken.net/apiAPI Key 填你的 KeyModel ID 填具体模型名。Codex 的auth.json路径一般在~/.codex/auth.json结构如下{ OPENAI_API_KEY: sk-你的TaoTokenKey, OPENAI_BASE_URL: https://taotoken.net/api }配置完成后可以用一条最小请求验证连通性避免后面排查性能问题时把网络问题误判成代码问题curl https://taotoken.net/api/v1/messages \ -H x-api-key: sk-你的TaoTokenKey \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-5-20250929, max_tokens: 64, messages: [{role: user, content: 回复 ok}] }返回里出现正常的content字段就说明链路通了。这一步很关键因为大列表优化往往要 Codex 连续分析多个文件、组件树和数据流上下文越长对稳定接入的要求越高。如果只是偶尔问一句用模型对话页面就够了https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。如果是长期做 React 仓库重构、多轮 Profiler 分析建议评估 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 连续上下文更适合同时看组件、Hooks 和多文件依赖。3. 可复制配置虚拟列表接入与关键参数这一节给可直接落地的配置。React 生态里成熟的虚拟列表方案有 react-window、TanStack Virtual、react-virtualized。下面以 TanStack Virtual 为例因为它对动态高度、横向滚动、表格场景支持都比较完整。先安装npm install tanstack/react-virtual然后写一个最小可用的虚拟列表组件。核心思路是数据有 10000 条但真正创建的 DOM 只保留可视区域加 overscan 的几十条滚动时动态替换当前可见区域的数据。import { useRef } from react; import { useVirtualizer } from tanstack/react-virtual; type User { id: string; name: string; email: string; status: string }; export function VirtualUserList({ users }: { users: User[] }) { const parentRef useRefHTMLDivElement(null); const rowVirtualizer useVirtualizer({ count: users.length, getScrollElement: () parentRef.current, estimateSize: () 48, overscan: 8, }); return ( div ref{parentRef} style{{ height: 600px, overflow: auto, contain: strict }} div style{{ height: ${rowVirtualizer.getTotalSize()}px, width: 100%, position: relative, }} {rowVirtualizer.getVirtualItems().map((virtualRow) { const user users[virtualRow.index]; return ( div key{user.id} style{{ position: absolute, top: 0, left: 0, width: 100%, height: ${virtualRow.size}px, transform: translateY(${virtualRow.start}px), }} UserRow user{user} / /div ); })} /div /div ); }关键参数说明。count是数据总条数不是渲染条数虚拟列表靠它计算总高度。estimateSize是单行预估高度固定行高直接返回常量动态行高可以传函数。overscan是可视区域外额外渲染的行数太小滚动会白屏太大又增加 DOM一般 5 到 10 比较稳。getScrollElement返回滚动容器注意容器必须有明确高度和overflow: auto否则虚拟化不生效。contain: strict是 CSS 层面的优化告诉浏览器这个容器的布局和绘制与外部隔离能减少重排范围。如果你用的是 react-window配置更简单import { FixedSizeList } from react-window; FixedSizeList height{600} itemCount{users.length} itemSize{48} width100% overscanCount{8} {({ index, style }) ( div style{style} UserRow user{users[index]} / /div )} /FixedSizeList两种方案都能把 DOM 数量从 10000 降到几十。选哪个看场景固定行高、结构简单用 react-window动态高度、表格、横向虚拟化用 TanStack Virtual。接入后记得把列表容器的key从数组 index 换成user.id这是虚拟列表稳定复用的前提。4. 验证请求与成功结果渲染计数与滚动帧率对比优化不能靠感觉要有可对比的数据。先做渲染计数。给UserRow加一个简单的计数逻辑用useRef记录渲染次数或者直接在组件里console.count(UserRow)。优化前10000 条数据首次渲染控制台会打印上万次优化后只打印可视区域加 overscan 的几十次。这个对比最直观。function UserRow({ user }: { user: User }) { console.count(UserRow render); return ( div classNamerow span{user.name}/span span{user.email}/span span{user.status}/span /div ); }再做滚动帧率对比。打开 Chrome DevTools 的 Performance 面板点击录制滚动列表 5 秒停止录制看 FPS 图表。优化前10000 行 DOM 滚动时 FPS 经常掉到 20 到 30Long Task 频繁出现Rendering 占比很高。优化后DOM 稳定在几十个滚动 FPS 基本能维持在 55 到 60Long Task 明显减少。如果还有掉帧继续看是 Scripting 高还是 Rendering 高。还可以用requestAnimationFrame手动测帧间隔let last performance.now(); let frames 0; function loop(now: number) { frames; if (now - last 1000) { console.log(FPS:, frames); frames 0; last now; } requestAnimationFrame(loop); } requestAnimationFrame(loop);优化前后各跑一次把 FPS、首次渲染耗时、DOM 节点数三个指标记下来。如果数据量增加 10 倍DOM 数量却基本不变说明虚拟化真正生效了。这一步也是给 Codex 提供反馈的关键不要只说「页面有点卡」而是把 Profiler 结果、组件数量、数据量、单次更新耗时一起给它优化才有明确目标。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth接入和优化过程中几类报错最容易混淆逐个对照。401 Unauthorized。多数是 Key 没填对、复制时带了空格或者 Base URL 写成了带路径的完整地址。检查ANTHROPIC_AUTH_TOKEN或OPENAI_API_KEY是否是控制台新建的 KeyBase URL 是否严格为https://taotoken.net/api不要多加/v1之外的路径。如果用的是 Claude Code确认settings.json里字段名是ANTHROPIC_AUTH_TOKEN而不是ANTHROPIC_API_KEY两者不通用。local proxy failed。通常是本地网络或代理配置干扰了请求也可能是客户端把 Base URL 指向了本地端口。先确认配置里没有多余的本地代理地址再确认getScrollElement返回的容器真实存在。虚拟列表场景下如果滚动容器ref没绑上虚拟化不生效页面会表现为「配置了但没效果」容易被误判成接口问题。reading choices 报错。一般出现在响应结构不符合预期时客户端解析choices字段失败。检查请求体里model是否拼写正确messages是否是数组max_tokens是否为正整数。如果用的是 Anthropic 兼容格式响应字段是content而不是choices客户端选错协议就会报这个错。OAuth 相关报错。Claude Code 首次启动可能引导登录如果已经用 Key 配置要确认没有残留的 OAuth 缓存覆盖配置。检查~/.claude目录下的凭据文件必要时清理后重新写入settings.json。Codex 的auth.json同理确认OPENAI_BASE_URL和OPENAI_API_KEY同时存在缺一个都会走默认端点导致失败。排查顺序建议固定为先验证 Key 和 Base URL再验证模型名最后才怀疑代码。大列表优化本身不涉及网络但接入不稳会让你把时间浪费在错误的方向上。6. 语义一致 CTA把大列表规则写进 AGENTS.md 并持续验证优化做完还要防止 Codex 下次生成代码时又退回老路。最有效的办法是把大列表规则写进项目根目录的AGENTS.md让 Codex 每次处理前端列表时都遵守# Frontend Large List 规则 - 超过 100 条数据的列表必须评估 Virtual List - 列表 key 必须使用稳定业务 ID禁止默认用数组 index - 大型列表禁止一次性创建全部 DOM - 后端大数据列表必须评估分页或 Cursor Pagination - Row 组件避免无意义复杂计算复杂逻辑下沉到数据层 - 禁止每一行默认挂载大型 Modal 或全局事件监听 - React.memo 必须结合真实重渲染问题使用不盲目加 - 性能优化前必须查看 Profiler 或 Performance 数据 - 大列表修改后必须用真实数据规模测试至少覆盖 100/1000/5000 条这样 Codex 后续就不会只以「页面能显示出来」作为完成标准。测试数据也别只用 10 条 mock至少覆盖 100、1000、5000 三档观察首次渲染耗时、滚动 FPS、搜索响应、内存占用和组件重渲染次数。如果数据量涨 10 倍而 DOM 基本不变说明虚拟化真正起作用了。需要长期做 React 仓库重构、多轮 Profiler 分析的话Coding Plan 的连续上下文更适合同时分析组件树、数据流和多文件依赖https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。临时验证某个模型对虚拟列表代码的理解用模型对话页面即可https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。接入文档和 API Key 管理分别在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 和 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。最后留一个实用技巧每次让 Codex 改大列表前先要求它输出一份「渲染地图」——总数据量、实际 DOM 数量、是否虚拟化、key 是否稳定、一次状态更新影响多少行、是否存在每行复杂计算或 Modal。先确认瓶颈是数据量、DOM、计算还是网络再动手重构。顺序固定为确认数据规模、限制接口返回量、分页或 Cursor、虚拟列表、稳定 key、减少 Row 复杂度、定位重复渲染最后才考虑 memo 和 useMemo。反过来先疯狂加 memo往往只能获得很有限的改善。