Sunday面试指南

Vue 3 的 Diff 算法怎么工作?最长递增子序列有什么用?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:Vue 3 更新列表时,Diff 是怎么做的?

🙋‍♂️ 我:比较新旧节点,通过 key 找到相同节点,再更新 DOM。

🧑‍💻 面试官:旧顺序是 A、B、C、D、E,新顺序是 A、C、D、B、E。每个节点都找到了,为什么还需要最长递增子序列?

🙋‍♂️ 我:为了减少节点移动。

🧑‍💻 面试官:具体留下哪些节点、移动哪个?最长递增子序列是在给 key 排序吗?

答好这道题,要分开「身份相同」和「位置合适」。key 帮你复用节点,最长递增子序列帮你少移动已经复用的 DOM。

面试速答(60 秒版)

Vue 3 处理带 key 的子节点时,会先比较两端可以直接匹配的部分,再处理中间发生变化的节点。

处理中间部分时,它通过 key 查找对应节点,更新可以复用的节点,删除不存在的旧节点,并识别需要新建的节点。对于可以复用、但顺序发生变化的节点,还需要安排 DOM 移动。

最长递增子序列就是在这一步发挥作用:把新节点顺序对应的旧位置记下来,找出其中相对顺序已经正确的一组节点,让它们保持不动,其余节点再移动。

因此,它不是把 key 排序,也不是让所有节点不移动。实际写列表时,要使用能稳定表示数据身份的 key;如果列表会增删、重排,就不能随意拿数组下标当身份。

Q277 面试速答总览:已逐项目视核对对象、标签、箭头与正文关系。

知识点详解:从一次列表重排理解 Vue 3 的 Diff

本文沿着 Vue core v3.5.22 的 renderer 源码 中带 key 子节点的更新分支讲解。Vue 3 还有编译优化、静态提升等机制,这里不把它们都塞进同一道题。

比较之前,先明确“相同节点”是什么意思

咱们假设页面有五条任务,key 分别是 A、B、C、D、E。旧顺序是 A、B、C、D、E,调整后变成 A、C、D、B、E。

任务 B 只是换了位置,它还是原来的任务。如果类型与 key 都能对应,Vue 就有机会复用原来的节点,再更新属性和内容,而不是把 B 销毁以后重新创建。

因此,Diff 不只是比较字符串。它需要确认节点身份,再安排对应的更新。假如 key 相同但组件类型变了,也不能据此认为原组件必然复用。

先跳过两端,再处理中间

这组数据的开头 A 没变,末尾 E 也没变。Vue 3 会先同步这些可以直接对应的头部、尾部节点。

剩下的变化就集中在旧的 B、C、D 和新的 C、D、B。

如果比较到后来,旧节点先用完了,剩余新节点就是插入;新节点先用完了,剩余旧节点就是删除。只有两边中间都还存在未处理节点,才需要继续建立新节点 key 到位置的映射。

接着遍历旧中间节点:旧 B 可以找到新 B,旧 C 可以找到新 C,旧 D 可以找到新 D,于是都能更新并复用。找不到对应项的旧节点才会被卸载。

同时还会记录:按照新顺序排列的这些节点,原来分别位于什么位置。 这份记录才是后面判断移动的依据。

Q277 知识点示意:旧 A B C D E;新 A C D B E;旧位置 0 2 3 1 4,无错误跨节点连线。

为什么 2、3、1 里要留下 2、3?

为了方便推演,给旧列表的位置从 0 编号:A 是 0,B 是 1,C 是 2,D 是 3,E 是 4。

新中间顺序是 C、D、B,所以对应的旧位置就是 2、3、1。源码内部记录会使用位置加一等实现细节,咱们这里保留原位置数字,方便理解。

其中 2、3 是递增的,表示 C 原来就在 D 前面,现在也仍然在 D 前面。它们之间的相对顺序没有问题,可以留下。

B 原来在 C、D 前面,现在需要放到 D 后面。因此,只移动 B,就能得到 A、C、D、B、E。不需要再把 C、D 各移动一次。

最长递增子序列找的是这样的“相对顺序已正确的最大一组”,不是连续片段,也不是 key 的大小顺序。源码再从后往前处理,用后面的节点作为插入位置参考,完成新增和必要移动。

如果没有发生顺序变化,就不必为了形式计算一遍 LIS。这个优化针对的是需要移动的中间部分,不能背成“所有 Diff 都必然执行 LIS”。

Q277 知识点示意:已目视核对技术关系;概念图不是实测结果。

面试官继续追问

为什么数组下标当 key 容易出问题?

假设在列表开头插入一条任务,原来下标为 0 的任务变成了下标 1。下标对应的身份变了,组件可能按位置复用给另一条数据,输入状态等就可能错位。稳定 ID 才更符合这组数据的身份。

key 能保证 DOM 完全不用更新吗?

不能。key 只是身份线索,节点内容、属性、子节点仍可能需要更新。稳定身份也不代表位置没有变化。

Vue 3 用的是 Vue 2 那套双端比较吗?

不能直接套旧版口诀。本题讲的是 Vue 3 的头尾同步、中间映射和必要时 LIS 的处理路径。面试时先说明版本和更新场景,别把两份实现混在一起。

面试速记卡

  • 节点身份:类型与 key 是否能对应,不是只看内容相似。
  • 处理顺序:先同步头尾,再处理剩余中间部分。
  • key 映射:帮助寻找可复用节点,不负责给数据排序。
  • LIS:保留相对顺序已正确的一组节点,减少必要移动。
  • 列表 key:优先稳定业务 ID,重排列表不要随意使用下标。

公司面试真题

这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历