Set不是Diff算法,而是高效支撑Diff中“存在性判断”的辅助工具,适用于快速查重、提取唯一标识、计算增删集合三类场景,但不能替代LIS等移动优化逻辑。

Set 本身不是 Diff 算法,但它能高效支撑 Diff 中的“存在性判断”环节——尤其在处理无序、去重、批量比对类场景时,比遍历或 indexOf 快得多。真正落地的关键,是把 Set 当作辅助工具,嵌入到 Diff 流程中,而不是替代 Diff。
什么时候该用 Set?明确适用边界
Set 适合解决以下三类子问题:
- 快速判定某个值是否存在于旧数据中:比如在 diff 子节点时,需频繁查 new 节点的 key 是否已在 old 列表里出现过
-
提取唯一标识集合做预处理:例如从 oldChildren 提取所有 key 构建
new Map(oldKeys.map(k => [k, null]))或直接用new Set(oldKeys) -
计算纯粹的“增删集合”(不关心顺序和复用):如权限列表变更、标签集合更新等非 UI 渲染类差异,可直接用
setA.difference(setB)类逻辑
实际代码中怎么嵌入 Set?以列表 diff 为例
假设你正在手写一个简化版 patch 函数,处理带 key 的子节点更新:
将一个或多个 UI 原型图、线框图、产品截图或设计稿转换为标准化、可直接交付的 PRD 风格文档,供产品、设计、前端和测试共同使用。适用于 Codex 需要分析 PC 网页、桌面端、移动端、响应式页面、独立弹窗、表单、表格、看板、数据大屏或多页面流程截图,还原页面结构与交互,强制套用固定文档模板,抽取跨页面共享规则,或把一组原型沉淀为单页规范文档加整体业务流程文档的场景。不适用于纯视觉审美点评、代码实现、接口设计、埋点方案、竞品分析或无 UI 输入的业务需求创作。
- 先用
const oldKeySet = new Set(oldChildren.map(n => n.key))构建查找索引 - 遍历
newChildren时,用oldKeySet.has(newNode.key)判断是否复用,O(1) 时间完成每次查找 - 同时维护一个
usedKeys = new Set(),每复用一个就 add 进去;最后遍历oldChildren,对 key 不在usedKeys中的节点触发删除
这比每次用 oldChildren.findIndex(n => n.key === k)(O(n))快得多,尤其当列表长度达百级时效果明显。
注意:Set 不能替代 key + LIS 移动优化
仅靠 Set 只能回答“这个 key 存不存在”,但无法回答“它该不该移动”或“移动到哪”。比如新顺序是 [a,b,c],旧顺序是 [c,a,b],Set 能告诉你三个 key 都存在,但无法推导出只需把 c 移到开头——这事还得靠最长递增子序列(LIS)算法定位不动节点。Set 是前置准备,LIS 是后续调度,二者分工明确。
与 generic-diff 或 jsdiff 库的关系
像 generic-diff 这类通用库底层也可能用 Set 加速对象键比对,但对外封装的是完整 diff 结果(含操作类型、位置、上下文)。你在业务中若只需“哪些项新增/缺失”,用原生 Set 自己写几行就搞定;若需“第几行改了几个字符”或“对象深层属性变化路径”,那就得交给专业 diff 库。别为简单需求引入重型工具,也别用 Set 去硬刚结构化差异。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










