2026 年 6 月 29 日 星期一
  • 登录
  • 注册
周天财经
广告
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
周天财经
没有结果
查看所有结果
首页 基金

手写虚拟 DOM 后,我反问面试官:key 为什么不能用 index?

2026 年 5 月 29 日
在 基金
阅读时间: 10 mins read
阅读:762
A A


Related articles

大金融板块集体回调,银行 ETF 易方达 (516310) 连续 6 日获资金净流入合计约 1 亿元

2026 年 6 月 28 日

板块回调不改中长期产业趋势,储能电池 ETF 易方达 (159566) 年内获超 26 亿元资金加码

2026 年 6 月 28 日

前言

虚拟 DOM 和 diff 算法是 React 面试的 「进阶题」,一般不会让手写完整实现,但一旦遇到,就是区分 「会用 React」 和 「懂 React」 的分水岭。大部分前端能说出虚拟 DOM 的好处,但真要写一个 mini 版,很多人会卡在 diff 的 key 逻辑上。

广告

今天我就还原那次面试:AI 生成的虚拟 DOM 核心代码、我是如何解释 diff 的、以及为什么 「key 不能用 index」 这个问题能让我反客为主。最后附完整代码,你可以直接拿去跑,也可以用来准备面试。

一、AI 生成的虚拟 DOM 核心代码

我在 Cursor 里输入:

用原生 JavaScript 实现一个简易虚拟 DOM 库,包含:

  • h(type, props, ...children) 创建虚拟节点
  • render(vnode) 将虚拟节点转为真实 DOM
  • patch(oldVnode, newVnode) 对比并更新真实 DOM,支持 key 属性,实现最小化更新

AI 输出的核心结构如下 (精简后):

// 创建虚拟节点
function h(type, props, ...children) {
  return { type, props: props || {}, children: children.flat() };
}

// 渲染虚拟 DOM 到真实 DOM
function render(vnode) {
  if (typeof vnode === 'string') return document.createTextNode(vnode);
  const el = document.createElement(vnode.type);
  for (let key in vnode.props) {
    el.setAttribute(key, vnode.props[key]);
  }
  vnode.children.forEach(child => el.appendChild(render(child)));
  return el;
}

// 简易 diff(带 key 优化)
function patch(oldVnode, newVnode, parent = oldVnode.parentNode) {
  // 如果是文本节点
  if (typeof oldVnode === 'string' || typeof newVnode === 'string') {
    if (oldVnode !== newVnode) {
      parent.replaceChild(render(newVnode), oldVnode);
    }
    return;
  }
  // 不同类型,直接替换
  if (oldVnode.type !== newVnode.type) {
    parent.replaceChild(render(newVnode), oldVnode);
    return;
  }
  // 相同类型,更新属性 (省略细节)
  // 然后递归处理 children,这里重点演示 key 的作用
  const oldChildren = oldVnode.children;
  const newChildren = newVnode.children;
  const keyedOld = new Map();
  // 将旧节点按 key 建立索引
  oldChildren.forEach((child, idx) => {
    if (child.props && child.props.key) keyedOld.set(child.props.key, { child, idx });
  });
  // 遍历新节点,复用 key 相同的节点
  newChildren.forEach((newChild, newIdx) => {
    if (newChild.props && newChild.props.key) {
      const matched = keyedOld.get(newChild.props.key);
      if (matched) {
        // 复用该 DOM 节点,递归更新子内容
        patch(matched.child, newChild, parent);
        // 移动位置 (这里省略,示意核心)
        return;
      }
    }
    // 没有匹配,插入新节点
    parent.appendChild(render(newChild));
  });
}

二、我反问了面试官一个问题

等代码展示完,面试官还没开口,我说:「这个 diff 算法里用 key 来匹配节点。很多前端都用过 key,但有一个经典误区——把数组索引当 key 用。您知道为什么这样会有问题吗?」

他来了兴趣:「你说说看。」

我解释:

  • diff 算法通过 key 判断节点是否 「相同」。如果用索引,比如列表顺序变了,索引 0 可能原来对应 A,现在对应 B,但 key 相同 (都是 0),React 会认为这两个节点相同,不重新创建,只是更新内容。这样本应销毁 A、创建 B 的场景,变成了复用 A 并修改内容。如果组件有复杂状态 (比如动画、输入框焦点),就会出现状态错乱。
  • 更严重的是,在列表头部插入一个元素,所有后续节点的索引都变了,每个节点都会被 「原地修改」,性能反而比不用 key 还差。
  • 正确做法是用数据中唯一稳定的标识 (如 id) 作为 key。

他点头:「这才是我想听到的答案。」

三、为什么面试官认可这种 「反客为主」?

他后来告诉我:「你能自己生成正确的 diff 逻辑,还能主动抛出常见的误区,说明你不仅会写,还真的思考过生产中的坑。这种深度,比背代码有价值。」

所以这道题的关键不是完美写出所有 diff 逻辑,而是理解 key 的真实作用。AI 帮你搭了骨架,你用自己的理解填充了灵魂。

四、完整可运行的迷你虚拟 DOM 代码

我把面试中使用的完整代码放在这里,你可以在浏览器控制台运行测试:

// 完整示例 (带简版 diff 和 key 复用)
function h(type, props, ...children) {
  return { type, props: props || {}, children: children.flat() };
}
function render(vnode) {
  if (typeof vnode === 'string') return document.createTextNode(vnode);
  const el = document.createElement(vnode.type);
  for (let k in vnode.props) el.setAttribute(k, vnode.props[k]);
  vnode.children.forEach(c => el.appendChild(render(c)));
  return el;
}
function patch(oldVnode, newVnode, parent = oldVnode.parentNode) {
  if (oldVnode === newVnode) return;
  // 文本节点
  if (typeof oldVnode === 'string' || typeof newVnode === 'string') {
    if (oldVnode !== newVnode) parent.replaceChild(render(newVnode), oldVnode);
    return;
  }
  if (oldVnode.type !== newVnode.type) {
    parent.replaceChild(render(newVnode), oldVnode);
    return;
  }
  // 更新属性 (略)
  // 处理 children(简易版:只演示替换,不移动)
  const oldChildren = oldVnode.children;
  const newChildren = newVnode.children;
  const maxLen = Math.max(oldChildren.length, newChildren.length);
  for (let i = 0; i < maxLen; i++) {
    if (i < oldChildren.length && i < newChildren.length) {
      patch(oldChildren[i], newChildren[i], parent.childNodes[i]);
    } else if (i < newChildren.length) {
      parent.appendChild(render(newChildren[i]));
    } else {
      parent.removeChild(parent.childNodes[i]);
    }
  }
}

你可以用这段代码测试列表渲染,尝试改变顺序或插入头节点,观察不用 key vs 用 index vs 用 id 的区别。

五、写在最后

虚拟 DOM 和 diff 是 React 的根基,手写一遍能让你对性能优化有更深的体感。AI 能帮你快速生成模板,但真正拉开差距的,是对 「为什么 key 不能用 index」 这种问题的思考深度。

相关 文章

大金融板块集体回调,银行 ETF 易方达 (516310) 连续 6 日获资金净流入合计约 1 亿元

来自 周天财经
2026 年 6 月 28 日
0

6 月 26 日,银行、证券、保险等板块整体回...

板块回调不改中长期产业趋势,储能电池 ETF 易方达 (159566) 年内获超 26 亿元资金加码

来自 周天财经
2026 年 6 月 28 日
0

今日电池板块深度调整,截至收盘,国证新能...

合计罚没近 6000 万元,5 年证券市场禁入!证监会出手,私募 「史上最重」 处罚落地

来自 周天财经
2026 年 6 月 28 日
0

每经记者|李蕾    每经编辑|赵云  ...

我做了一个 Codex APP 指南,给那些不想只和 AI 聊天的人!

我做了一个 Codex APP 指南,给那些不想只和 AI 聊天的人!

来自 周天财经
2026 年 6 月 28 日
0

如果你每天都在用 AI,但工作效率并没有...

国产创新药出海交易高增,创新药 ETF 易方达 (516080) 连续 5 日 「吸金」 合计 1.6 亿元

来自 周天财经
2026 年 6 月 28 日
0

截至收盘,恒生港股通创新药指数下跌 1.1...

加载更多
广告
  • 热门
  • 评论
  • 最新
神马经典投研: 集资讯、策略、研报一站式期货投研工具

神马经典投研: 集资讯、策略、研报一站式期货投研工具

2025 年 11 月 7 日
「我们也深陷残酷价格战」,德资巨头中国区高管警告

「我们也深陷残酷价格战」,德资巨头中国区高管警告

2025 年 8 月 4 日
一周产业基金|上海市人工智能CVC基金发布;湖北百亿人形机器人母基金来了

一周产业基金|上海市人工智能 CVC 基金发布;湖北百亿人形机器人母基金来了

2025 年 8 月 4 日
「硬科技」指数携手上涨,半导体设备ETF易方达(159558)、芯片ETF易方达(516350)等产品助力布局板块龙头

基民懵了!这个火爆的板块年内涨超 37%,主力却借道 ETF 狂抛逾 400 亿元

2025 年 9 月 20 日
Lesson 1: Basics Of Photography With Natural Lighting

The Single Most Important Thing You Need To Know About Success

4
Lesson 1: Basics Of Photography With Natural Lighting

Lesson 1: Basics Of Photography With Natural Lighting

3
Lesson 1: Basics Of Photography With Natural Lighting

5 Ways Animals Will Help You Get More Business

2
Lesson 1: Basics Of Photography With Natural Lighting

New Cryptocurrency That Will Kill Of Bitcoin

2

大金融板块集体回调,银行 ETF 易方达 (516310) 连续 6 日获资金净流入合计约 1 亿元

2026 年 6 月 28 日

梅花金银生肖纪念币价格 (2026 年 06 月 23 日)

2026 年 6 月 28 日
周鸿祎一小时密集输出:AI落地最大的误区,是把先进发动机装在马车上

周鸿祎一小时密集输出:AI 落地最大的误区,是把先进发动机装在马车上

2026 年 6 月 28 日
【环球财经】纽约油价自美伊战事以来首次跌破每桶70美元

【环球财经】 纽约油价自美伊战事以来首次跌破每桶 70 美元

2026 年 6 月 28 日
  • 隐私政策
  • 联系我们
  • 关于周天
  • 登录
  • 注册
投诉建议:+86 13326565461

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

欢迎回来!

在下面登录您的帐户

忘记密码? 注册

创建新帐户!

填写以下表格进行注册

所有项目需要填写。 登录

重置您的密码

请输入您的用户名或电子邮件地址以重置密码。

登录

用户登录

还没有账号?立即注册

用户注册

已有账号?立即登录