浏览器题考的不是名词量,而是能否把机制、触发条件、优化手段和度量对成一条线。这份清单按这条线整理高频考点。
浏览器题用四段式回答,比背定义稳:
| 段落 | 回答内容 | 作用 |
|---|---|---|
| 机制 | 内部怎么运作,涉及哪些进程/线程/阶段 | 证明理解 |
| 触发 | 什么操作会进入这个机制 | 证明能定位 |
| 手段 | 优化或规避的具体做法 | 证明能落地 |
| 度量 | 用什么指标验证有效 | 证明闭环 |
最容易被追问的是"你怎么知道是这一步慢的",所以每个机制都要能顺带说出一个 DevTools 面板或 API。
一句话:Chromium 用进程隔离把"页面崩溃"和"页面越权"都关在单个站点内。
| 进程 | 职责 |
|---|---|
| 浏览器主进程 | 地址栏、标签页管理、前进后退、文件对话框、部分网络调度 |
| 渲染进程 | 每个标签页一个,Blink 排版渲染 + V8 执行 JS |
| GPU 进程 | 光栅化与合成,所有页面共用 |
| 网络进程 | 走 NetworkService,处理请求、缓存、代理 |
| 插件 / Utility 进程 | PDF、编解码、不受信任务 |
渲染进程为什么要隔离
渲染进程内的线程:主线程(DOM、CSS、JS)、合成线程(滚动与动画)、光栅线程、Worker 线程池。理解这条分层才能解释"为什么 transform 动画不卡"。
一句话:HTML 变 DOM、CSS 变 CSSOM,合成渲染树后依次算样式、布局、绘制、合成。
HTML → DOM ┐
├→ Render Tree → Style → Layout → Paint → Raster → Composite
CSS → CSSOM┘
| 阶段 | 产物 | 主线程 |
|---|---|---|
| Style | 每个节点的计算样式 | 是 |
| Layout(回流) | 每个盒子的几何位置与尺寸 | 是 |
| Paint | 分层后的绘制指令列表 | 是 |
| Raster | 图层位图 | 光栅线程 |
| Composite | 按图层树合成一帧 | 合成线程 |
哪一步触发回流:改动影响几何的属性,如 width/height/padding/margin/border/font-size/display/position/top/left,或读写会强制同步布局的 API(offsetTop、getBoundingClientRect)。
哪一步只触发重绘:改动只影响外观,如 color/background/visibility/box-shadow/outline。
哪些连重绘都不需要:transform、opacity、filter 由合成线程处理,改的是图层变换矩阵或透明度,主线程完全不参与。
一句话:优化的本质是减少主线程参与,把变化交给合成线程。
transform/opacity 能走合成,是因为它们只改变图层的变换和混合参数,已有位图可以直接复用;改 left/top 则要重新布局、重新绘制整层。will-change 是提前告知浏览器"这个属性要变",让它预先分层;滥用等于常驻图层,只在动画开始前加、结束后移除。DocumentFragment 或 innerHTML 一次插入,动画用 requestAnimationFrame,列表虚拟化,离屏元素 content-visibility: auto。追问:transform 一定不回流吗——改变的是布局无关属性才对;同时改 width 依然回流。
一句话:每轮取一个宏任务执行完,清空所有微任务,需要时渲染一帧,再进入下一轮。
宏任务 → 清空微任务 → [rAF → Style/Layout/Paint] → 空闲回调 → 下一个宏任务
| 类型 | 成员 |
|---|---|
| 宏任务 | 整段 script、setTimeout、setInterval、MessageChannel、I/O、UI 事件回调 |
| 微任务 | Promise.then/catch/finally、queueMicrotask、MutationObserver、await 后续代码 |
| 渲染前 | requestAnimationFrame |
| 渲染后空闲 | requestIdleCallback |
Promise.then 为什么比 setTimeout(0) 先执行
then 的回调进微任务队列,当前宏任务结束后立刻被清空;setTimeout 的回调是下一个宏任务,还要排队,嵌套调用还有 4ms 最小延迟钳制。
浏览器与 Node 的差异
setImmediate)→ close。每个阶段结束清空 process.nextTick 队列,再清空微任务队列;nextTick 优先于 Promise。setTimeout(0) 与 setImmediate 在 I/O 回调内的顺序仍不固定。requestAnimationFrame 在哪:在样式计算与布局之前、下一帧绘制之前执行,回调参数是帧时间戳;同一帧多次调用只执行一次,后台标签页暂停。适合动画,不适合做数据计算。
一句话:按"容量、生命周期、是否随请求发送、是否同步"四个维度选型。
| 方案 | 容量 | 生命周期 | 随请求发送 | API |
|---|---|---|---|---|
| Cookie | 约 4KB/条,单域名约 50 条 | 可设 Expires/Max-Age,否则会话级 | 是,同源请求自动带 | 同步 |
| localStorage | 5–10MB | 永久,除非手动清除 | 否 | 同步 |
| sessionStorage | 约 5MB | 标签页会话,关闭即清 | 否 | 同步 |
| IndexedDB | 配额制,通常数百 MB 起 | 永久 | 否 | 异步、事务 |
HttpOnly(JS 读不到)、Secure(仅 https)、SameSite(CSRF 防护)。跨标签页通信
storage 事件:同源的其他标签页触发,当前页自己不触发;event.key/newValue 可用,但删改频繁时建议只当信号再读一次 localStorage。BroadcastChannel:同源任意标签页广播,API 简单,推荐首选。SharedWorker、Service Worker 做消息中转。window.postMessage:跨源 iframe 或 window.open 场景,必须校验 origin。一句话:V8 分代回收,新生代复制、老生代标记清除并整理;泄漏的本质是"还有引用可达但业务上已经不用"。
常见泄漏场景
| 场景 | 原因 |
|---|---|
| 未解绑事件监听 | 全局对象或 DOM 一直持有回调 |
| 定时器未清理 | setInterval 持有闭包作用域 |
| 全局变量缓存 | 只增不减的 Map/数组 |
| Detached DOM | JS 变量还引用已移除的节点 |
| 闭包持有大对象 | 回调长期存活 |
console.log | DevTools 打开时保留引用 |
排查:Performance Monitor 看内存曲线是否锯齿上行,Heap Snapshot 采三次对比"只增不减"的对象,Allocation Timeline 定位分配来源。
一句话:三项 Core Web Vitals 分别衡量"看到主要内容快不快、点了之后响应快不快、页面稳不稳"。
| 指标 | 含义 | 良好阈值 | 优化方向 |
|---|---|---|---|
| LCP | 视口内最大内容元素的渲染时间 | ≤ 2.5s | 图片压缩与 fetchpriority、预连接、字体、SSR、CDN、减少阻塞资源 |
| INP | 交互到下一次绘制的延迟 | ≤ 200ms | 拆分长任务、scheduler.yield、事件处理轻量、减少 DOM 规模 |
| CLS | 累积布局偏移 | ≤ 0.1 | 图片/视频写宽高或 aspect-ratio、字体 size-adjust、不在顶部动态插入 |