【前端】【面试】前端 Diff 相关考题及答案

发布于:2025-03-29 ⋅ 阅读:(31) ⋅ 点赞:(0)

一、前端 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 方案,通过:
    1. 层级限制:只比较同级元素,避免跨层 Diff 计算。
    2. Key 机制:通过 Key 标记唯一性,提高 Diff 效率。
    3. 复用相同类型节点:相同类型的节点不会被删除,而是直接更新属性。
(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 算法的工作流程是什么?

答:

  1. 找出不同的 Virtual DOM 节点
  2. 同级对比,不同则直接替换,相同则递归对子节点 Diff。
  3. Key 机制,如果 Key 变化,直接重新创建节点。
  4. 批量更新,合并多次变更,提高性能。
(2) React 中如何优化 Diff 过程,提高性能?

答:

  1. 使用 Key(避免重新创建整个列表)。
  2. PureComponent / React.memo(减少不必要的渲染)。
  3. useMemo / useCallback(缓存计算结果)。
  4. shouldComponentUpdate / React.memo(控制更新逻辑)。
(3) Vue 2.x patch 方法如何执行 Diff?

答:

  1. 找到相同类型的节点,如果不同则直接替换。
  2. 比较 Props 变化,更新变更的属性。
  3. 递归对子节点 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 算法,并能结合实际业务场景优化。