一、前端 Diff 相关考题及答案
Diff 算法用于对比数据差异,常见于前端框架(React、Vue)、版本控制(Git)、数据同步(WebSocket)等场景。以下是常见考题及答案:
二、考题与答案
(一)基础概念题
(1) 什么是 Diff 算法?前端为什么需要 Diff?
答:
Diff(Difference)算法用于比较两个数据结构的不同点。前端需要 Diff 主要是为了高效更新界面,减少不必要的 DOM 变更,提高性能。例如:
- React 和 Vue 使用 Diff 算法进行 虚拟 DOM 对比,只更新变化的部分,而不是重新渲染整个 DOM。
- Git 版本控制使用 Diff 算法找出代码变更,提高协作效率。
(2) 传统 Diff 算法的时间复杂度是多少?为什么 React 选择了 O(n) 的 Diff 策略?
答:
- 传统 Diff 算法 采用动态规划,计算两个树结构的最优匹配,时间复杂度为 O(n³),对于大规模 DOM 计算成本过高。
- React 的 Diff 策略 采用 O(n) 的 Diff 方案,通过:
- 层级限制:只比较同级元素,避免跨层 Diff 计算。
- Key 机制:通过 Key 标记唯一性,提高 Diff 效率。
- 复用相同类型节点:相同类型的节点不会被删除,而是直接更新属性。
(3) 解释 React Fiber 的 Diff 机制,与传统 Diff 有何不同?
答:
React Fiber 是对 React 16 以后 Diff 机制的优化,核心区别:
- 传统 Diff:同步更新,整个组件树 Diff 需要一次性计算完成。
- Fiber Diff:引入 时间切片(Time Slicing),可以分片执行 Diff 计算,避免主线程长时间卡顿,提高响应速度。
(4) Vue 2.x 和 Vue 3.x 的 Diff 机制有何差异?
答:
- Vue 2.x:使用
patch
递归对比,核心是 双端 Diff,采用 头尾指针优化,减少不必要的遍历。 - Vue 3.x:引入 静态节点标记(Block + Fragment),跳过不变的部分,减少 Diff 计算量,提高渲染性能。
(二)手写算法题
(1) 实现一个最简单的对象 Diff 计算函数,返回两者的不同点。
function objectDiff(obj1, obj2) {
let diff = {};
for (let key in obj1) {
if (obj1[key] !== obj2[key]) {
diff[key] = { old: obj1[key], new: obj2[key] };
}
}
for (let key in obj2) {
if (!(key in obj1)) {
diff[key] = { old: undefined, new: obj2[key] };
}
}
return diff;
}
// 示例
console.log(objectDiff({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 }));
// 输出:{ b: { old: 2, new: 3 }, c: { old: undefined, new: 4 } }
(2) 给定两个 JSON 对象,找出不同的 key 并返回差异结果。
function jsonDiff(json1, json2) {
let keys1 = Object.keys(json1);
let keys2 = Object.keys(json2);
let added = keys2.filter(k => !keys1.includes(k));
let removed = keys1.filter(k => !keys2.includes(k));
let modified = keys1.filter(k => keys2.includes(k) && json1[k] !== json2[k]);
return { added, removed, modified };
}
// 示例
console.log(jsonDiff({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 }));
// 输出:{ added: ['c'], removed: [], modified: ['b'] }
(3) 设计一个简单的列表 Diff 算法,输出添加、删除和移动的操作。
function listDiff(oldList, newList) {
let changes = { added: [], removed: [], moved: [] };
let oldMap = new Map();
oldList.forEach((item, index) => oldMap.set(item, index));
newList.forEach((item, newIndex) => {
if (!oldMap.has(item)) {
changes.added.push(item);
} else {
let oldIndex = oldMap.get(item);
if (oldIndex !== newIndex) {
changes.moved.push({ item, from: oldIndex, to: newIndex });
}
}
});
oldList.forEach(item => {
if (!newList.includes(item)) {
changes.removed.push(item);
}
});
return changes;
}
// 示例
console.log(listDiff(['a', 'b', 'c'], ['b', 'c', 'd']));
// 输出:{ added: ['d'], removed: ['a'], moved: [] }
(三)React/Vue 相关题
(1) React 组件更新时,Diff 算法的工作流程是什么?
答:
- 找出不同的 Virtual DOM 节点。
- 同级对比,不同则直接替换,相同则递归对子节点 Diff。
- Key 机制,如果 Key 变化,直接重新创建节点。
- 批量更新,合并多次变更,提高性能。
(2) React 中如何优化 Diff 过程,提高性能?
答:
- 使用 Key(避免重新创建整个列表)。
- PureComponent / React.memo(减少不必要的渲染)。
- useMemo / useCallback(缓存计算结果)。
- shouldComponentUpdate / React.memo(控制更新逻辑)。
(3) Vue 2.x patch
方法如何执行 Diff?
答:
- 找到相同类型的节点,如果不同则直接替换。
- 比较 Props 变化,更新变更的属性。
- 递归对子节点 Diff,采用 双端 Diff(头尾指针优化)。
(四)工程实践题
(1) Git 是如何实现 Diff 的?
答:
- Git 使用 最小编辑距离算法(LCS) 找出两个文件的差异。
- 通过 哈希 快速定位变更部分,提高 Diff 速度。
(2) 如何利用 Diff 算法优化前端的表格数据渲染?
答:
- 只更新 变化的单元格,避免整个表格重渲染。
- 使用 虚拟滚动(Virtual Scroll)优化大数据量渲染。
(3) 在多人协作开发时,如何利用 Diff 提高代码合并效率?
答:
- 采用 Git Diff + Merge 方式,自动合并非冲突代码。
- 代码 Review 时使用 Diff 工具,提高可读性。
(4) WebSocket 传输数据时,如何使用 Diff 仅发送变化的部分?
答:
- 通过 JSON Diff 计算变化的字段,只传输增量数据。
- 采用 二进制格式(如 Protobuf) 降低传输大小。
三、总结
- Diff 算法优化了前端框架、版本控制和数据同步。
- 面试时要 理解原理、手写 Diff 算法,并能结合实际业务场景优化。