聊天讨论 面试官让我优化一个卡死的表格,我打开 Claude 五秒重写——他放下咖啡问我要不要来当组长

193577746(kyriewen) · July 15, 2026 · 10 hits

上周去面试一家做 SaaS 的中厂,二面是一位四十出头的前端负责人。

寒暄结束,他直接把笔记本转过来推到桌子中间:

"这是我们线上一个真实的报表页,10 万行数据,滚起来直接卡死。你帮我看看怎么优化。用什么工具都行。"

最后那半句他说得很平淡,我当时没多想。

结果二十分钟后,他把咖啡杯往桌上一放,问了我一个我准备了整场面试都没想到的问题。


一、题目本身:一个"教科书级"的坑

页面代码大概长这样(我做了脱敏改写):

function ReportTable({ rows }: { rows: Row[] }) {
  // rows.length ≈ 100_000
  return (
    <table>
      <thead>...</thead>
      <tbody>
        {rows.map((row) => (
          <tr key={row.id}>
            <td>{row.name}</td>
            <td>{row.amount}</td>
            <td>{row.status}</td>
            <td>
              <Select options={statusOptions} value={row.status} />
            </td>
            <td>
              <DatePicker value={row.updatedAt} />
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

写过一年 React 的都能一眼看出问题:

  1. 10 万行一次性渲染 → 首屏就是白屏 6-8 秒
  2. 每行还挂着 <Select> <DatePicker> 这种带弹层的重组件
  3. key 没问题,但 diff 也扛不住 40 万个 DOM 节点
  4. 滚动过程再触发 hover / active,浏览器直接躺平

搁在教科书里,标准优化路径也有三条:

  • 虚拟滚动(react-window / @tanstack/react-virtual)
  • 时间切片渐进渲染(startTransition / requestIdleCallback
  • Canvas / 半 Canvas 渲染(AG Grid、Handsontable 那种)

三种我都懂,但每种都有细节:单元格要不要支持编辑?行高是否等高?要不要支持多选?滚动过程能不能容忍占位闪一下?

真让我在白板上写完再讲清楚三者的取舍——20 分钟够呛。


二、我做了一件"面试大忌"的事

我抬头问了一句:

"您刚才说,用什么工具都行?"

面试官点头。

于是我把自己的笔记本翻开,打开 Claude Code,把这段组件连同 Row 类型、业务约束一起丢了进去,只写了一句:

"React 18 + TS。10 万行表格,每行 5 列,其中两列有 Select 和 DatePicker。产品要求可以滚动、可以点某一行进入详情、后期可能要支持行内编辑。给我三种优化方案,并解释在哪种交互需求下谁最合适。"

大概 4 秒之后,屏幕上刷出了三段方案。

方案一:虚拟滚动(@tanstack/react-virtual)

import { useVirtualizer } from '@tanstack/react-virtual';

function ReportTable({ rows }: { rows: Row[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const rowVirtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 44,
    overscan: 10,
  });

  return (
    <div ref={parentRef} style={{ height: 600, overflow: 'auto' }}>
      <div style={{ height: rowVirtualizer.getTotalSize(), position: 'relative' }}>
        {rowVirtualizer.getVirtualItems().map((v) => {
          const row = rows[v.index];
          return (
            <div key={row.id} style={{
              position: 'absolute', top: 0, left: 0,
              transform: `translateY(${v.start}px)`,
              height: v.size, width: '100%',
            }}>
              {/* row cells */}
            </div>
          );
        })}
      </div>
    </div>
  );
}

Claude 的注释里写:"最通用、社区最成熟。等高行下开箱即用,行内编辑也没问题。缺点是 Select / DatePicker 这类弹层组件在滚出可视区时会被销毁,弹层里的选中态要托管到外层 state。"

方案二:时间切片渐进渲染

function ReportTable({ rows }: { rows: Row[] }) {
  const [visibleCount, setVisibleCount] = useState(50);

  useEffect(() => {
    if (visibleCount >= rows.length) return;
    const id = requestIdleCallback(
      () => setVisibleCount((c) => Math.min(c + 200, rows.length)),
      { timeout: 100 }
    );
    return () => cancelIdleCallback(id);
  }, [visibleCount, rows.length]);

  return (
    <table>
      <tbody>
        {rows.slice(0, visibleCount).map((row) => (
          <tr key={row.id}>{/* cells */}</tr>
        ))}
      </tbody>
    </table>
  );
}

Claude 提示:"首屏最快,DOM 结构最简单,SEO 友好。但滚到最底部要 10+ 秒才能加载完,用户拖滚动条会看到断层。适合'看不完但不能少'的报表——比如打印导出前的预览页。"

方案三:半 Canvas 渲染(AG Grid / 自研)

Claude 直接建议接第三方库,并给出了两条使用理由:

"如果表格未来要支持行内编辑、多选、复制粘贴、列拖拽——自己造轮子会一路踩坑。AG Grid 的 Community 版本免费够用,缺点是包体积 500KB+,且样式覆盖成本高。"

我把屏幕转向面试官。他没说话,只是往前挪了挪坐姿。


三、真正让面试官愣住的,不是 AI 给的答案

我接着说:

"我倾向于方案一。因为您说了'后期可能要支持行内编辑'——Canvas 方案改起来太重,切片方案在编辑焦点跳出时会重排。虚拟滚动只要把弹层组件的开合状态提到外层就行。要不咱们跑一下 Performance 看看?"

我把三种方案都在他们的测试环境跑了一遍,Chrome DevTools 打开 Performance。

  • 未优化:首屏 6.8 秒,滚动帧率 8 FPS
  • 方案一:首屏 320 ms,滚动稳定 60 FPS
  • 方案二:首屏 90 ms,但滚到底部要 12 秒
  • 方案三:首屏 480 ms,60 FPS,但 bundle 大了 480 KB

首屏时间从 6.8 秒压到 320 毫秒,快了 21 倍

面试官这时候放下了咖啡杯,问了我第一个"意外"的问题:

"你怎么判断把弹层 state 提到外层就够了?"

我说:

"我没直接判断。Claude 给方案后我又追问了一次——'如果同一行的 Select 打开时用户滚出可视区,会发生什么?'它回答说组件会 unmount,弹层直接消失。所以我在方案一里加了一句:把 openId 提到父组件用 Portal 渲染弹层。这样滚出去也不会关。"

面试官盯着我看了两秒,问了第二个问题:

"你组里其他前端,也是这么用 AI 的吗?"

我如实说:不是。大部分同事要么完全不用,要么直接把 AI 的答案 Ctrl+C Ctrl+V 提交。

他"嗯"了一声,然后是第三个问题——就是那个让我离开会议室之后琢磨了一路的问题:

"我们前端组长下个月转岗。你要不要考虑一下这个位置?"


四、我在地铁上想明白的事

回程地铁上我一直在想:他到底看中了什么?

不是虚拟滚动。方案二方案三我都答得上来,任何一个高级前端都能。

不是 AI。他们组里肯定有人 Claude、Cursor 都开着。

想通之后我总结出三点,写在这里给同样在跳槽的朋友:

1. 他看的是信息带宽,不是答题速度

传统面试考察的是"你脑子里存了多少知识"。但当所有人脑子里都能调用同一个 AI 的时候,考察点就变成了"你能在单位时间内提出多好的问题"。

我丢给 Claude 的那条 prompt 里有五个关键要素:

  • 技术栈(React 18 + TS)
  • 数据量级(10 万行 5 列)
  • 复杂组件(Select、DatePicker)
  • 交互约束(可滚动、可点击、可能要编辑)
  • 判断标准

如果我只写"帮我优化这个卡顿的表格",Claude 给的答案就是水平线。

面试官看到的其实是:这个人的 prompt 里,藏着五年前端经验。

2. 他看的是验证能力,不是采纳能力

我在讲方案时说过一句话:"这是 Claude 给的,但我用 Performance 面板验证过了。"

这句话之所以关键,是因为它划出了两类人:

  • 把 AI 输出当权威,直接提交
  • 把 AI 输出当同事的初稿,看完了再判断

后一种人,团队里其实不多。而这种人是能带团队的。

3. 他看的是你怎么让 AI 干活,不是你会不会用 AI

前端组长这个岗位在他心里对应的画像大概是:

"一个能把 AI 当放大器用的人,一个人的产出等于三个人。而且他知道什么时候不该信 AI——比如弹层脱离视口、比如包体积不能牺牲。"

我在展示解题过程里,无意间把这两点都露出来了。


五、给还在准备面试的前端

如果你也在跳槽,尤其在中厂到大厂的过渡区间,几个我踩过之后的建议:

能带电脑就带电脑。 越来越多的公司允许现场用工具了。别怕面试官觉得你依赖 AI——他们在乎的是你怎么用。

练习"提问"比练习"手写"回报大。 手写 Promise、手写深拷贝、手写 EventEmitter,AI 三秒能解。但"给出三种方案 + 比较适用场景 + 结合数据量和交互约束"这种 prompt,得真练。

每一次 AI 给答案后,都强迫自己追问一次"如果 X 前提不成立,会怎样"。 例如:

  • 如果表格行高不等,虚拟滚动会怎样?
  • 如果用户网速慢,切片渲染的最后一屏会怎样?
  • 如果产品要打印全表,Canvas 方案怎么办?

这是把 AI 的天花板变成你的地板的唯一方法。

准备一段"我怎么用 AI"的故事。 不是理论,是具体的一次协作过程。这段故事的价值,比你把 Fiber 原理背下来大得多。


尾声

2026 年了,考察前端的方式已经变了。

变化不是"你会不会用 AI",而是"你能不能带着 AI,做出一个人做不完的事"。

面试官那天放下咖啡的那 2 秒,我大概能听见他脑子里在想什么:

"这人来了之后,我这个季度的排期能砍一半。"

能被这么想,比手写十道八股都值钱。

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.