面试备战录

150 阅读10分钟

1、什么是 DOM、CSSOM?为什么它们合成后的 Render Tree 不包含 display: none 的元素?

答:DOM是由HTML解析生成的树,CSSOM是由CSS解析生成的规则树,浏览器会将两者结合生成Render Tree,用来进行布局和绘制。Render Tree只包含需要渲染的节点,因此像display: none的元素不会进入Render Tree(不参与布局和绘制),而visibility: hidden的元素仍然会在Render Tree中,只是最后不绘制。

  • DOM (Document Object Model):浏览器把HTML 文档解析成的树形结构。每个标签、文本、注释都会成为DOM节点
  • CSSOM (CSS Object Model):浏览器把CSS 样式表解析成的树状结构。CSSOM会描述选择器和规则。
  • Render Tree (渲染树):浏览器会把DOM + CSSOM合并,得到一个只包含“可见节点”的树。这个树才是真正用来计算布局(Layout)绘制(Paint)的。

为什么Render Tree不包含display: none的元素?

  • display: none的语义:该元素完全从视觉渲染树中移除。不占据空间,不渲染,不触发布局计算。
  • 浏览器性能考虑:如果把display: none的元素也算进去会额外进行布局和绘制 → 浪费性能,所以浏览器在构建Render Tree时会直接跳过。
  • visibility: hidden的区别:visibility: hidden的元素仍然在Render Tree中,会占据布局空间,只是不绘制(透明处理)display: none的元素直接不进入Render Tree,连布局都不参与。

2、什么是回流(Reflow)和重绘(Repaint)?分别在什么时候发生?

答:回流(Reflow) 是浏览器重新计算元素的几何属性(大小、位置),会引发布局变化;重绘(Repaint)是元素外观样式变化但不影响布局时发生。回流的代价更大。常见触发回流的操作有:修改宽高、margindisplay、读取offset系列属性等;常见触发重绘的操作有:修改颜色、背景、visibility等。优化上我们可以通过减少 DOM 操作、批量修改样式、使用transform/opacity替代位置变化来降低回流和重绘的成本。

  • 回流(Reflow / Layout)
    • 浏览器重新计算元素的几何属性(位置、大小、宽高、位置关系)。
    • 会导致 Render Tree重新计算 + 页面布局重新生成。
    • 代价比重绘大。
  • 重绘(Repaint)
    • 元素的几何属性没有变,只是样式(颜色、背景、阴影等)改变。
    • 浏览器只会在已有的几何位置上重新填充像素。
    • 比回流轻量。
  • 触发回流的操作
    • 页面首次渲染(必然发生)。
    • 添加/删除DOM节点
    • 改变元素的尺寸(width/height/padding/margin/border)。
    • 改变元素的显示状态(display: none → 显示)。
    • 获取某些属性时(浏览器需要强制刷新计算):
      • offsetTop / offsetLeft / offsetHeight / offsetWidth
      • scrollTop / scrollHeight
      • getComputedStyle()
    • 改变窗口大小 / 改变字体大小。
  • 触发重绘的操作
    • 改变color、background-color、visibility
    • 改变box-shadow、outline等。
    • 不涉及几何位置的纯样式变化。

优化点

  • 避免频繁操作DOM
    • 批量修改样式 → 使用className一次性替换。
    • 多次DOM操作 → 先用documentFragmentcloneNode,最后一次性挂载。
  • 减少回流触发
    • 避免逐次访问offsetXXX等属性,先缓存值。
    • 避免逐条修改样式,最好合并修改。
  • 使用transform/opacity替代位置调整
    • 例如用translate移动元素,不会触发回流,只触发合成层变换。
    • CSS动画性能优化时很常用。
  • 使用will-change或单独的合成层
    • 让浏览器提前优化某些属性的变化

2、什么是合成层(Compositing Layer)?它与 GPU 加速有啥关系?

答:合成层(Compositing Layer) 是浏览器渲染中被GPU独立管理的图层。当元素被提升为合成层时,它的动画(如transform、opacity)可以由GPU直接合成,不需要CPU重新布局和绘制,从而提升性能,实现硬件加速。常见触发方式有will-change、transform: translateZ(0)等。但层数过多会增加显存和合成开销,所以要合理使用。

  • 合成层(Compositing Layer) 浏览器渲染页面时,会把DOM + CSS转换成渲染树,然后经历以下步骤:
    • 布局(Layout):计算元素的大小、位置。
    • 分层(Layering):根据CSS特性(如transform, opacity等)决定哪些元素需要单独的图层(Layer)
    • 绘制(Paint):把每个图层的内容绘制到位图。
    • 合成(Compositing)GPU将这些图层合成在一起,形成最终的屏幕画面。

其中,合成层(Compositing Layer) 就是被单独提升出来,由GPU管理和渲染的图层。

  • 为什么要有合成层?
    • 如果所有元素都在同一个层里,每次元素变化(例如动画、滚动)都会导致 重绘/回流,性能很差。
    • 把某些元素单独提到合成层,这些元素的变化(如transform: translate())可以直接由GPU在独立层里处理,不用重新走布局和绘制。
    • 结果就是:动画更流畅、性能更高
  • 如何触发合成层(Layer Promotion),常见能让元素成为合成层的CSS/属性有:
    • transform: translateZ(0) / translate3d(0,0,0)
    • will-change: transform / opacity
    • 使用CSS动画的 transform、opacity
    • <video>, <canvas>, <iframe> 通常也会独立成层

注:滥用这些属性会导致层过多,增加内存和合成开销,反而掉帧。

  • 它和GPU加速的关系
    • 普通渲染流程:CPU完成布局 + 绘制 → 把位图交给GPU合成。
    • 合成层存在时:某些元素交由GPU单独处理,移动/透明度变化时GPU直接做合成,而不依赖CPU重绘。这就是我们常说的 “硬件加速”
    • 合成层 ≠ GPU 加速,但合成层是GPU加速生效的前提。
    • 提升到合成层后,能避免频繁的CPU绘制,充分利用GPU的能力。

2、浏览器中如何做到 JS 执行与页面渲染之间的协调?会不会互相阻塞?

答:JS执行和页面渲染都在浏览器主线程上运行,不能并行。如果JS执行时间过长,会阻塞渲染,导致掉帧或页面卡顿。浏览器通过事件循环协调JS和渲染:在每一帧(约 16.6ms)结束后,先清空JS微任务,再进行样式计算、布局和绘制。为了避免阻塞,可以使用requestAnimationFrameWeb WorkerGPU 合成层优化。

  • 浏览器主线程职责:
    • JS 执行(JS 引擎,比如V8
    • 样式计算(Recalculate Style)
    • 布局(Layout)
    • 绘制(Paint)
    • 合成(Composite Layers)

也就是说,JS和渲染管线(排版、绘制)其实是在同一条主线程上运行的。

  • JS与渲染的协调机制。浏览器使用了事件循环(Event Loop)+ 渲染帧机制来协调:
    • 事件循环:JS任务(宏任务/微任务)按队列依次执行。
    • 渲染时机:浏览器通常会以每秒 60 帧(16.6ms 一帧)为目标,在一次循环结束后:先清空微任务队列,再进行样式计算 → 布局 → 绘制 → 合成
    • JS 和渲染不会并行(主线程只有一个),而是交替运行。

所以,如果JS长时间执行(比如死循环、大计算任务),主线程被占满,浏览器就没法插入渲染任务 → 页面卡死、掉帧。或者大量DOM操作、复杂计算 → 页面卡顿

  • 浏览器的优化手段
    • 任务切片:把大任务拆成小块,用setTimeout/requestIdleCallback分批执行。
    • requestAnimationFrame:保证 JS 在浏览器下一帧渲染前运行,避免无效计算。
    • Web Worker:把耗时计算放到后台线程,不阻塞UI渲染
    • GPU 合成层:把transform、opacity 动画交给GPU合成,绕过主线程的布局和绘制

3、Web Worker 和主线程通信原理是怎样的?适合做什么?

答:Web Worker是浏览器提供的多线程机制,运行在主线程之外,主要通过postMessage / onmessage进行通信,底层基于消息队列和结构化克隆。它不能直接操作DOM,适合处理计算密集型或长时间运行的任务,比如大数据计算、图像处理等,从而避免阻塞主线程,保证页面的流畅交互。

  • Web Worker是浏览器提供的一种多线程方案,允许JS在后台开辟一个独立线程运行。
  • 特点:
    • 运行在主线程之外,不会阻塞UI渲染和事件响应。
    • 不能操作DOMBOM(没有document、window)。
    • 只能通过消息传递(postMessage + onmessage) 与主线程通信。
  • Web Worker和主线程之间的通信依赖事件机制
    • 主线程 → Worker
    const worker = new Worker('worker.js');
    worker.postMessage({ type: 'START', payload: 100 });
    
    • Worker → 主线程:
     self.onmessage = function (e) {
         const { type, payload } = e.data;
         self.postMessage(`收到消息:${type}, ${payload}`);
     }
    
    • 主线程监听返回:
    worker.onmessage = function (e) {
        console.log('来自 worker 的消息:', e.data);
    }
    
  • 使用场景:由于Worker不能操作DOM,它主要用于 计算密集型任务
    • 大数据计算(如加密、压缩、复杂数学计算)。
    • 图片/音视频处理(转码、滤镜)。
    • AI/机器学习推理(TensorFlow.js等在Worker中跑)。
    • 长轮询/大文件分片上传等网络任务。
    • 避免JS长任务阻塞主线程,提升UI流畅度。
  • 缺点:
    • 不能直接操作 DOM / BOMdocument、alert、window不可用)。
    • 不能访问localStorage / sessionStorage(可用 IndexedDB 代替)。
    • 受同源策略限制,加载的脚本必须同源。

4、requestAnimationFrame 和 setTimeout 区别在哪?哪个更适合动画?

答:setTimeout基于事件循环,执行时间不准,不能保证和屏幕刷新同步。requestAnimationFrame是专为动画设计的 API,和显示器刷新率对齐,浏览器会在每一帧渲染前调用回调,保证动画流畅,并且在页面不可见时自动暂停,节能省资源。

  • setTimeout
    • 原理:把回调函数放入 宏任务队列,由事件循环调度执行。
    • 缺点:
      • 定时器不准:受事件循环和主线程阻塞影响,实际执行时间 ≥ 设定时间。
      • 与屏幕刷新不同步:浏览器一般60Hz(16.6ms 一帧),但setTimeout(fn, 16)不能保证和屏幕渲染对齐,可能掉帧、卡顿。
      • 即使页面不可见(切到后台Tab),它依然在运行(高耗能)。
  • requestAnimationFrame(rAF)
    • 原理:告诉浏览器“我需要执行一个动画”,浏览器会在下一次重绘前调用回调。
    • 特点:
      • 自动跟随显示器刷新率(通常60Hz ≈ 16.6ms一帧),更流畅。
      • 节能:后台标签页或隐藏元素不会执行(浏览器自动优化)。
      • 时间戳参数:回调函数会收到一个高精度时间戳,可用于计算动画进度。

5、为什么说CSS动画(如 transform/opacity)性能比JS更优?

答:CSS动画性能优于JS动画的核心原因是:transformopacity的变化不会触发回流/重绘,只触发合成(Compositing),可由GPU加速。CSS动画可以交给浏览器渲染线程独立执行,不依赖JS主线程,更流畅。JS动画虽然灵活,但会占用主线程,容易掉帧。

  • CSS动画可能由浏览器优化,甚至跳过JS主线程
    • CSS动画(transform / opacity)可以交给浏览器 **渲染引擎或合成线程(Compositor Thread)**直接运行。
    • 即使JS主线程卡住,CSS动画也能流畅运行。
  • CSS动画更容易触发GPU加速
    • 常见的transform(位移、缩放、旋转)和 opacity(透明度变化),只会触发合成层更新
    • 不会引起回流(Reflow)重绘(Repaint)
    • 浏览器可以把这些动画交给GPU做硬件加速,性能更高、掉帧更少。
  • 浏览器能进行内部优化(比如帧率控制、合成优化)
    • CSS动画运行时,浏览器可以自动调度动画帧,保证60fps
    • JS动画即使用requestAnimationFrame,也要走JS主线程,容易受垃圾回收、长任务影响。
  • 为什么只提到transform / opacity
    • 因为 只有这两个属性的动画不会引起回流/重绘。
    • 高性能属性:transform、opacity
    • 慢的属性:top/left、width/height、margin/padding等,会引起回流(Reflow),性能差。
  • JS 动画的优势
    • 更强的控制力(暂停、反转、动态计算值)。
    • 可以与 业务逻辑、用户交互 强绑定。
    • 适合复杂场景(物理模拟、游戏、拖拽)。