面试备战录

60 阅读3分钟

1、频繁的操作 DOM 为什么会降低性能?

  • 频繁的操作 DOM 会导致多次重排和重绘 --> CPU忙碌 --> JS 主线程被卡,浏览器帧率掉到 60fps(帧) --> 最终表现为:卡顿、掉帧、页面延迟响应
  • 频繁的DOM访问&修改会导致 JS 引擎与渲染引擎交互频繁;
  • 浏览器的JS 引擎渲染引擎是分线程的,每次你访问 element.offsetTop 或修改 innerHTML,会强制渲染线程计算布局,叫做 layout thrashing(布局颠簸)
  • 虚拟 DOM 则可以把这些 DOM 操作集中在内存中 diff,再“合并批量”进行真实 DOM 更新,避免 thrashing。

2、Diff 算法

答:Diff 算法就是用来比较新旧两个虚拟 DOM 树,找出两者的差异(变化),然后只把必要的更新操作应用到真实 DOM 上,从而避免不必要的 DOM 操作,提升性能。

Diff 算法的核心思想

  • 分层比较
    • 对比两个虚拟 DOM 树时,Diff 算法通常是“分层比对”,只对相同层级的节点进行比较,不会跨层级查找节点。
  • 同级比较规则
    • 在同一层节点的比较中,判断节点是否相同的主要依据是节点类型(tag)和 key
  • 最小化更新
    • 尽可能重用旧节点,只对变化的地方进行替换、移动、删除或新增操作。

Vue 和 React 中 Diff 的优化规则

  • 相同节点(同标签同 key),只更新属性和子节点。
  • 不同节点,销毁旧节点,创建新节点。
  • key 的作用:帮助快速定位变化节点,优化节点的复用和移动,避免不必要的销毁和重建。

Diff 算法流程:

<!-- 旧树 -->
<ul>
  <li key="a">A</li>
  <li key="b">B</li>
  <li key="c">C</li>
</ul>
<!-- 新树 -->
<ul>
  <li key="b">B</li>
  <li key="a">A</li>
  <li key="d">D</li>
</ul>
  • 对比<ul>,相同节点,继续对子节点比较。
  • 子节点对比根据 key:
    • key "a" 和 "b" 顺序变了,Diff 发现只需移动节点,不用销毁重建。
    • key "c" 在新树不存在,需删除。
    • key "d" 是新节点,需新增。

3、为什么要用虚拟 DOM

  • 解决直接操作真实 DOM 性能瓶颈;
  • 虚拟 DOM 不是直接操作浏览器 DOM,而是生成一种纯 JS 对象树表示,可以更容易实现跨平台渲染(比如 React Native 就是用虚拟 DOM 思想映射到原生控件),也方便后续优化。
  • 简化开发思维和维护:开发者不需要手动管理 DOM 细节,只写声明式代码,框架负责最小化更新和状态同步,代码更简洁,易维护。
  • 支持组件化、响应式、声明式 UI:虚拟 DOM 支持更好地组织代码和组件,框架可以跟踪依赖,实现响应式更新,自动刷新页面。

不使用虚拟 DOM 的优点

  • 直接操作真实 DOM 或使用模板编译生成高效代码:像 Vue 3 的编译优化,模板编译成精细的渲染函数,尽量减少 diff,甚至跳过虚拟 DOM 生成直接操作 DOM 节点。
  • 静态分析和编译时优化:有些框架依赖于编译时分析,减少运行时的开销,比如 Svelte、SolidJS,运行时不维护虚拟 DOM 树,性能更好。
  • 轻量级和性能需求:虚拟 DOM 带来便利但也有开销,某些性能要求极高或场景简单的项目,直接操作 DOM 或用简单的模板更新更轻量。