渲染性能与内存泄漏

共 47 题
📑 题目列表 47 题
#
★★★

1. Long Animation Frames(LoAF)API 在长帧的诊断

Long Animation Frames(LoAF)API 如何诊断长帧?与 Long Tasks 有何区别?

  • long-animation-frame 条目的定义与内容
  • 与 longtask 的差异(渲染纳入、归因更细)
  • 在卡顿归因中的应用

LoAF(Long Animation Frames)API 通过 PerformanceObserver 监听 long-animation-frame 条目:当一帧的渲染任务(脚本 + 样式/布局/绘制 + 渲染)总耗时超过 50ms 时产生条目,包含 startTime、duration、脚本明细(script 数组:URL、函数名、耗时、类型)与渲染阶段耗时(styleAndLayout、paint)。与 Long Tasks 的区别:longtask 只覆盖"脚本任务",LoAF 以"帧"为单位把脚本与渲染合并统计,更贴近用户感知的卡顿(一帧内脚本+渲染超时);且 LoAF 提供 attribution(具体脚本来源),可直接定位到第三方脚本或业务代码。

诊断应用:用 LoAF 条目定位"帧超时"的组成——是脚本(哪个文件/函数)还是渲染(样式计算/布局/绘制);归因到第三方(ad/analytics SDK 的脚本 URL 直接可见);配合 Event Timing(交互到下一帧)把 INP 劣化映射到具体长帧。注意:LoAF 条目在页面无交互的空闲场景不产生(按帧渲染评估)、跨浏览器支持正在扩展(Chromium 已支持);工程上把它作为"长任务 + 帧级渲染"的统一诊断源。

考察长帧诊断的新 API:LoAF 以帧为单位合并脚本与渲染并带归因,回答应对比 longtask 的差异并给出归因用法。

#
★★★

2. Time Slicing/Concurrent Mode React 在大型列表渲染的工程价值

Time Slicing 与 React 的并发渲染(Concurrent Mode)在大型列表渲染中有哪些工程价值?

  • 时间切片与并发渲染的原理
  • React 18+ 并发特性(transition)对列表的价值
  • 与虚拟滚动的配合边界

Time Slicing(时间切片)指把大渲染任务拆成小片、在帧间隙执行(React 并发渲染的内部机制),主线程不被长时间独占,输入事件与渲染可插队;React 18+ 的并发特性(startTransition/useTransition、并发 root)让"非紧急更新"(如大列表筛选、复杂计算)标记为低优先级渲染——可被紧急更新(输入)中断,从而保持交互响应。大型列表场景的价值:输入筛选大列表时,输入处理优先(INP 改善),列表渲染后台分批完成,避免"键入即卡顿"。

配合与边界:并发渲染解决"主线程调度"问题,不解决"DOM 节点数量"问题——超大列表仍需虚拟滚动(只渲染视口内节点);transition 的渲染可能被中断重来(计算成本增加)、Suspense 与并发配合处理异步数据;工程实践:大列表的过滤/排序用 transition 包裹、配合 memo/useMemo 减少重渲染、虚拟化 + 并发双管齐下。价值总结:把"渲染"从阻塞主线程的巨石变为可中断的调度任务。

考察并发渲染的调度价值:可中断渲染保障交互响应,与虚拟滚动分工(调度 vs 节点量),回答应体现两者的边界。

#
★★★

3. useTransition 与 useDeferredValue 在非紧急渲染的工程价值

useTransition 与 useDeferredValue 在非紧急渲染中有哪些工程价值?

  • useTransition 标记更新优先级
  • useDeferredValue 延迟派生值
  • 两者的差异与适用场景

useTransition 返回 [isPending, startTransition]:把 startTransition 内的状态更新标记为"非紧急",可被紧急更新(输入、点击)打断,适合"由交互触发的重计算"(如搜索框输入触发大列表过滤——输入是紧急的,过滤渲染可延迟);useDeferredValue 接收一个值并返回"延迟版本":当值变化时先返回旧值渲染、后台用新值重新渲染(可被打断),适合"值本身是受控状态、派生计算重"的场景(列表过滤、图表数据),且不需要改事件处理逻辑(在派生层处理)。

差异:useTransition 控制"更新来源"(事件里包一下),useDeferredValue 控制"值消费"(渲染层延迟);前者适合有明确触发点,后者适合值链路过深不好包事件;两者都是"低优先级渲染 + 可中断",配合 memo 防止派生部分无谓重渲染。工程价值:把"输入响应性"与"重渲染成本"解耦——输入即时反馈(紧急),重渲染后台让步(非紧急),INP 与流畅度双赢;边界:值确实很旧时 UI 短暂滞后(需视觉反馈:isPending/旧值占位)、不适合必须同步的更新(表单校验结果)。

考察 React 并发 API 的精确分工:useTransition 包更新来源、useDeferredValue 延迟值派生,回答应说明差异与滞后权衡。

#
★★★

4. @tanstack/virtual 的 overscan 与 estimateSize 在动态高度列表的应用

@tanstack/virtual 的 overscan 与 estimateSize 在动态高度列表中有哪些应用?

  • 虚拟滚动的测量与渲染窗口
  • estimateSize 的动态高度估算与重测
  • overscan 的缓冲策略与权衡

虚拟滚动只渲染视口附近的行,@tanstack/virtual 的核心是"测量与定位":useVirtualizer 接收 count、getScrollElement、estimateSize(预估每行高度)与 overscan;动态高度列表(内容长度不一)用 estimateSize 返回估算值(如平均高度),滚动过程中对可见行用 measureElement 精确测量并缓存,未测行用估算值布局——实现"先估算后校正"的渐进精确。overscan 指定视口外额外渲染的行数(如上下各 5 行):缓冲滚动过程中的白屏窗口(快速滚动时元素提前就位),但增加 DOM 数量与渲染成本。

应用要点:动态高度下 overscan 适当加大(测量延迟更明显);estimateSize 的准确性影响跳动(估算偏差大 → 滚动位置校正跳动),可用缓存上次高度提高准度;测量用 ResizeObserver(@tanstack/virtual 内置)自动重测内容变化(如图片加载后高度变化);与并发渲染/内容安全(content-visibility)组合进一步降载。取舍:overscan 大 → 更顺滑但更重,按行高一致性(均匀/动态)与滚动速度权衡。

考察虚拟滚动的两个关键参数:estimateSize 的估算-校正机制与 overscan 的缓冲权衡,回答应体现动态高度场景的参数调优。

#
★★★

5. CSS Containment 与 content-visibility 跳过渲染

CSS Containment 与 content-visibility 如何跳过渲染?有何工程价值?

  • contain 的布局/绘制/尺寸隔离语义
  • content-visibility: auto 的渲染跳过机制
  • 长页面与列表的收益与边界

CSS Containment(contain: layout paint size 等)声明元素的内部渲染被"隔离":布局(layout)隔离使内部变化不影响外部布局、绘制(paint)隔离使内容裁剪在边界内、尺寸(size)隔离使元素尺寸独立于内容——浏览器据此跳过不必要的计算(元素在视口外时)。content-visibility: auto 是组合应用:视口外的元素跳过渲染(样式计算、布局、绘制)并保留尺寸占位(含 size containment),滚入视口时才渲染,长页面/长列表的初始渲染成本大幅下降(首屏时间与内存收益)。

工程价值:内容型长页(文章、信息流)用 content-visibility: auto 显著减少初始渲染工作量;配合虚拟滚动(虚拟化管"DOM 是否存在"、content-visibility 管"已存在 DOM 是否渲染")双管齐下;边界:auto 要求元素有确定的近似尺寸(否则滚动条跳动,用 contain-intrinsic-size 提供占位尺寸)、滚动性能受限的浏览器(Safari 支持较晚)、对"初始可见"内容无效(只对视口外);调试用 DevTools 的 Rendering 面板确认跳过。注意:content-visibility 减少渲染但不是"不加载"(资源仍会加载)。

考察渲染跳过技术:containment 隔离 + content-visibility 跳过视口外渲染,回答应包含 contain-intrinsic-size 与边界认知。

#
★★★

6. Bundle 体积预算(KB 级别)在 LCP 与 Lighthouse 评分的临界点

Bundle 体积预算(KB 级别)如何影响 LCP 与 Lighthouse 评分?临界点如何把握?

  • JS 体积对解析执行与 LCP 的影响机制
  • Lighthouse 评分与体积的关联
  • 预算临界点与业务权衡

JS 体积通过三条路径影响性能:传输时间(网络,尤其弱网)、解析编译(主线程占用,设备越弱越明显)、执行(阻塞交互)。Lighthouse 的评分模型对"未使用/过度 JS"敏感:Total Blocking Time 与脚本执行时长挂钩,体积超限直接拉低 performance 分数;LCP 方面,render-blocking 脚本延迟首屏内容、主线程解析占用推迟绘制。工程上常用经验阈值:首屏关键 JS gzip ≤ 170KB(Web Vitals 社区经验值,对应"中度设备可接受解析")、第三方脚本预算独立设限。

临界点把握:预算不是"越小越好"——功能需要体积支撑,预算应与业务价值匹配(数据指标:体积变化 ↔ 转化/留存);用 Lighthouse 的"脚本执行时间"审计与真实设备解析数据校准阈值(模拟中端设备验证);预算分桶(首屏关键 vs 懒加载 chunk)、按路由控制增量;临界点的本质是"在预算约束内做功能取舍":动态导入、移除冗余依赖、按需 polyfill 是放大预算的杠杆,预算数字本身随业务与设备基线演进。

考察体积与性能的量化关联:解析执行链路、Lighthouse 模型、经验阈值与业务权衡,回答应体现"预算为业务服务"的校准思维。

#
★★★

7. 性能预算(Performance Budget)的制定与 CI 阻断

性能预算如何制定?如何在 CI 中实现阻断?

  • 预算维度与基线的制定方法
  • CI 阻断的落地(体积/指标/LHCI)
  • 预算治理流程(评估、演进、豁免)

性能预算制定:先量化现状(RUM p75 与 Lab 基线、当前体积与请求数),再设定目标(按业务诉求与竞品:如 LCP ≤ 2.5s、首屏 JS ≤ 170KB gzip),预留缓冲(±10%)防止误报,按维度分层(体积/数量/指标/时间);关键指标选"用户可感知且可度量"的(LCP/INP/CLS + 体积),避免只盯单维度(体积达标但指标劣化)。CI 阻断落地:体积类用 size-limit/bundlesize(构建后断言返回码)、指标类用 Lighthouse CI(assertions 阈值)、资源类用 performance-budget.json(数量/大小);全部接入 PR 检查(状态必过)与主分支门禁。

治理流程:预算变更走评审(有依据的调整而非偷偷放宽)、超限时展示 diff 与定位报告(哪个文件/审计项)、豁免机制(标注原因与期限,到期回收);演进:定期用新基线重设预算(优化后收紧),预算与业务目标挂钩(性能分数 ↔ 转化)。本质:预算把性能从"口号"变成"可执行的门禁"。

考察性能预算的完整闭环:基线→目标→阻断→治理,回答应体现"可度量、可阻断、可演进"三要素。

#
★★★

8. performance.measure User Timing 在关键路径打点的工程价值

performance.measure(User Timing)在关键路径打点有哪些工程价值?

  • User Timing API 的 mark/measure 机制
  • 业务关键路径的埋点设计
  • 与 RUM、性能分析平台的集成

User Timing API 提供 performance.mark(name)(记录时间戳)与 performance.measure(name, startMark, endMark)(计算区间耗时),条目通过 getEntriesByType('measure')/PerformanceObserver 读取;它允许开发者给"浏览器不知道的业务阶段"打点——从点击提交到接口返回、从数据就绪到渲染完成、组件初始化耗时等,弥补浏览器内置 timing(导航/资源)不覆盖业务链路的空白。

工程价值:关键路径打点让性能分析从"页面级"细化到"业务动作级":首屏拆解(JS 启动 → 数据获取 → 渲染就绪)、交互链路拆解(点击 → 请求 → 响应 → DOM 更新 → 绘制)、组件级耗时(挂载/更新);配合 RUM:打点数据随上报进入分析平台(保留名称规范、去敏),建立"业务动作时间线"看板,定位"哪个环节占了大头";与 Web Vitals 互补(CWV 管体验指标、User Timing 管内部链路)。实践:命名规范(统一前缀、版本化)、mark 在关键代码路径对齐(如路由切换点)、measure 结果在测试中可断言(性能回归)。

考察 User Timing 的工程应用:mark/measure 机制、业务链路打点、与 RUM 集成,回答应强调"浏览器盲区内的业务时间线"。

#
★★★

9. FCP/LCP 的 75 百分位 在现代前端项目的工程价值

FCP/LCP 的 75 百分位(p75)在现代前端项目中有哪些工程价值?

  • 百分位统计的意义(分布而非均值)
  • p75 作为 CWV 官方口径的理由
  • 工程上的告警、对比与目标设定

p75(第 75 百分位)指"75% 的用户体验好于该值",相比均值(被极端值拉偏)与中位数(掩盖长尾劣化),p75 在"代表性"与"敏感性"间平衡:它代表"大多数用户(3/4)体验达标"的底线,同时捕捉到尾部劣化信号(长尾用户设备弱、网络差)。CWV 官方以 p75 为判定口径(LCP/INP/CLS 的 Good 阈值对应 p75),因为性能优化的目标是"让绝大多数用户体验好"而非"平均好"。

工程价值:告警(p75 超阈值即触发,尾部劣化可感知)、对比(版本/地区/设备间的 p75 差异更有决策意义)、目标设定(预算对齐 p75 而非均值,如"LCP p75 < 2.5s");配合维度下钻(分设备 p75:桌面优但移动 p75 超标 → 移动端专项);注意 p75 的样本量要求(CrUX 最低流量门槛、RUM 抽样误差控制)。工程实践:报告与告警默认 p75、评估优化效果看 p75 变化(而非均值或单次)、与 p50/p90/p95 组合呈现分布全貌(p50 看典型、p75 看底线、p95 看长尾)。

考察百分位统计的工程语义:p75 代表多数体验底线且捕捉长尾,回答应说明官方口径理由与多维组合分析。

#
★★★

10. Virtual Scroller(TanStack Virtual / react-virtuoso / @tanstack/react-virtual)

Virtual Scroller(TanStack Virtual / react-virtuoso 等)的原理与选型是什么?

  • 虚拟滚动的渲染窗口原理
  • 主流库的差异(TanStack Virtual 的无头、virtuoso 的自动测量)
  • 选型维度(动态高度、API、生态)

虚拟滚动(windowing)的原理:只渲染可视区域(视口 + overscan)内的行,用"占位容器总高度 + 绝对定位/transform 平移"模拟完整滚动条,DOM 数量恒定(几十个节点),支撑十万级列表;核心难点是行高测量——固定高度简单(总高 = 行数 × 行高),动态高度需估算-测量-校正(测量缓存)。主流库差异:TanStack Virtual 是 headless(无渲染、框架绑定:@tanstack/react-virtual 等),提供测量/定位原语,动态高度用 estimateSize + measureElement;react-virtuoso 组件化(自带测量、自动视口观察、平滑滚动与列表组件内置),上手快但定制受限;另有 react-window(轻量固定尺寸为主)、自研(极致定制)。

选型维度:动态高度比例(TanStack 灵活、virtuoso 自动)、与框架/渲染模式契合(headless vs 组件)、滚动交互复杂度(分组、粘性、加载更多)、体积与生态;工程要点:数据不可变(列表更新用 key 稳定)、图片等异步内容触发重测、与 content-visibility 组合、滚动位置保持(列表切换时 restore)。本质:虚拟滚动是"DOM 数量恒定 + 测量校正"的工程,选型取决于动态性与集成成本。

考察虚拟滚动原理与库选型:窗口渲染、动态高度测量、headless vs 组件化差异,回答应体现"按动态性与定制需求选型"。

#
★★★

11. 用 heap snapshot 与 Comparison 视图定位 detached DOM 树泄漏的完整流程

用 heap snapshot 与 Comparison 视图定位 detached DOM 树泄漏的完整流程是什么?

  • detached DOM 泄漏的特征与成因
  • 快照采集与 Comparison 对比方法
  • 从对比结果到根因的定位

detached DOM 泄漏指元素从文档移除后仍被 JS 引用(事件监听器、闭包、全局缓存、定时器、第三方库),GC 无法回收;特征:Heap Snapshot 中出现 Detached 类节点(带红色标记),内存随操作持续增长。完整定位流程:一是基线快照——进入页面稳定态后强制 GC(DevTools 的 Collect Garbage)拍快照 A;二是复现操作——反复执行"创建-销毁"场景(打开关闭弹窗/列表增删/路由往返);三是对比快照——再拍快照 B,用 Comparison 视图(按 All Objects 或 Detached 过滤)看新增的 detached 节点数量与大小,确认泄漏存在(Detached DOMTree 数量递增)。

四是定位根因——在 Comparison/Retainers 视图选中 detached 节点,查看 Retaining Tree(保持引用链):沿"被谁引用"链(window、闭包、监听器、缓存)找到泄漏源头代码(如 addEventListener 未移除、闭包持有大对象、全局数组堆积、DOM 引用存于 module 级变量);五是修复与验证——按引用链修复(清理监听/闭包/缓存),重新走"基线→复现→对比"确认 detached 不再增长。工具配合:Allocation instrumentation(分配时间线)辅助观察泄漏时段的分配热点。

考察内存泄漏定位的方法论:基线-复现-对比三步、Retaining Tree 追引用链,回答应体现"对比驱动 + 引用链定位"的完整闭环。

#
★★★

12. 闭包、事件监听、定时器、订阅未释放四类常见泄漏模式的特征与各自排查入口

闭包、事件监听、定时器、订阅未释放四类常见泄漏模式各有什么特征?排查入口是什么?

  • 四类泄漏的机制特征
  • 各自在 DevTools 中的排查入口
  • 预防性实践(清理模式)

四类常见泄漏:闭包泄漏——外部函数持有大对象/大数组,内部函数闭包引用导致外部作用域无法释放(特征:内存稳定增长、与特定操作相关;入口:heap snapshot 的 Retaining Tree 查 Closure 作用域引用);事件监听泄漏——DOM 移除后 addEventListener 未 remove(含匿名函数无法移除),元素被监听器引用无法回收(特征:重复创建销毁后 Detached 增多;入口:Performance/Event Listeners 面板查冗余监听器、快照中 listener 对象);定时器泄漏——setInterval/setTimeout 未清理,回调持续执行并引用状态(特征:无操作时 CPU/内存仍增长;入口:Performance 面板的 Timers 记录、代码检索未清理定时器);订阅未释放——事件总线/Store/Observable 订阅后未取消(特征:长会话增长、与组件生命周期关联;入口:订阅管理代码审计、快照中 Subscription/事件对象增长)。

排查通用入口:内存面板(heap snapshot 对比、allocation timeline、Detached 过滤)定位"什么在增长",再沿引用链(Retainers)落到代码;预防性实践:统一清理模式(useEffect 清理函数、组件卸载 dispose、定时器引用保存以便清除、订阅返回取消函数)、依赖注入的订阅源管理(单例事件总线用弱引用或显式退订)、CI/内存回归(快照对比自动化)。核心:四类泄漏的共同机制是"引用未释放",排查入口是"增长识别 + 引用链追踪"。

考察泄漏模式的分类认知:四类的机制特征、DevTools 入口、清理模式,回答应体现"引用未释放"的统一机制与预防。

#
★★★

13. Allocation instrumentation on timeline(分配时间线)定位持续分配对象的方法

Allocation instrumentation on timeline(分配时间线)如何定位持续分配的对象?

  • 分配时间线的记录机制(按时间采样分配)
  • 与堆快照的互补(时间 vs 静态)
  • 定位持续分配热点的方法

Allocation instrumentation on timeline 是 DevTools Memory 面板的时间线记录模式:记录运行期间所有 JS 对象分配(含调用栈),以时间线形式呈现分配速率(蓝条高度 = 分配量);重放(Replay)后可按时间窗口查看"哪些函数分配了对象",也可打开某个分配点查看其调用栈。与 heap snapshot 的互补:快照是"静态截面"(此刻哪些对象存活)、分配时间线是"动态过程"(何时何地分配)——持续分配(如字符串拼接循环、缓存无界写入)在时间线上表现为"稳定高分配速率",快照对比则确认"存活增长"。

定位方法:记录一段时间(复现目标操作)→ 观察时间线的高分配区间 → 选择区间查看分配热点(函数名/文件)→ 结合栈定位代码(如循环内重复创建大对象、每次渲染新建缓存);区分"临时分配"(被 GC、无害但增加压力)与"持续存活"(泄漏):配合快照/GC 确认。实践:优化持续分配(复用对象、避免循环内新建、流式处理),减少 GC 压力与内存峰值;分配时间线成本高(记录开销),用于"定位阶段"而非常驻监控。

考察分配分析的动态视角:时间线记录分配热点与调用栈、与快照的"过程 vs 截面"互补,回答应体现定位-确认-优化流程。

#
★★★

14. scheduler.yield 与 scheduler.postTask 在主任务切片的工程价值

scheduler.yield 与 scheduler.postTask 在主任务切片中有哪些工程价值?

  • Scheduler API 的 yield 与 postTask 语义
  • 优先级队列与中断控制
  • 与 setTimeout/rAF 的现代取舍

Scheduler API(Chromium 系支持,可 polyfill):scheduler.yield() 是"让出"原语——当前任务暂停让主线程处理更高优先级的输入/渲染,之后继续(比 setTimeout(0) 更贴近"让出后立即续跑"且不受最小节流影响);scheduler.postTask(fn, { priority }) 按优先级(user-blocking/user-visible/background 及可自定义)调度任务,支持 AbortSignal 取消与 delay。组合价值:postTask 把任务按优先级排队(非紧急的解析/预取放 background,不抢交互)、yield 在长任务中定期让出(配合循环切片),实现"主线程预算管理"。

与 setTimeout/rAF 的取舍:setTimeout 是"定时器语义"(最小延迟 4ms 嵌套节流、无优先级)、rAF 是"渲染前回调"(适合与绘制对齐的更新),Scheduler 提供"按优先级的中断式调度"——更精细的主线程治理;工程价值:长任务拆分(yield)、非紧急任务降级(postTask background)、可取消(AbortSignal)与响应式调度(优先级继承);边界:浏览器支持需 polyfill(scheduler-polyfill)、与 React 并发调度并存时的协调、优先级滥用反而劣化;验证用 INP/长任务指标。

考察现代调度原语:yield 让出、postTask 优先级队列、与旧 API 的取舍,回答应体现"主线程预算管理"的新范式。

#
★★★

15. ResizeObserver 与 IntersectionObserver 在性能敏感列表的应用

ResizeObserver 与 IntersectionObserver 在性能敏感列表中如何应用?

  • IntersectionObserver 的懒加载与渲染节流
  • ResizeObserver 的动态测量与响应
  • 组合应用与回调节流

IntersectionObserver 异步观察元素与视口(或祖先容器)的交叉状态:列表场景用于——图片/内容懒加载(进入视口才加载,isIntersecting 触发)、渲染节流(可视区外行的重渲染降级)、无限滚动(底部占位元素进入视口触发加载更多);回调异步批量派发(每帧一次),天然比 scroll 监听 + getBoundingClientRect 高效(无需主线程逐帧计算)。ResizeObserver 观察元素尺寸变化:虚拟列表的动态高度测量(内容变化 → 高度变化 → 重排位置)、响应式布局的精确适配(容器尺寸变化调整列数/密度)。

组合应用:性能敏感列表 = 虚拟化(窗口渲染)+ IntersectionObserver(进入视口加载/停用开销)+ ResizeObserver(测量与自适应);回调注意节流(Observer 回调已按帧批量,但处理逻辑仍要轻量:只做标记、实际工作移到 rAF/批量阶段)、避免回调内直接触发布局读取(layout thrashing);与 content-visibility 协作(视口外跳过渲染);注意观察器数量上限(超量时复用单一观察器 + entry.target 分发)。

考察两个观察器 API 的列表性能应用:交叉状态驱动懒加载与节流、尺寸变化驱动测量与自适应,回答应体现组合与回调轻量化。

#
★★★

16. WebGPU 在前端图像处理与游戏渲染的工程价值

WebGPU 在前端图像处理与游戏渲染中有哪些工程价值?

  • WebGPU 的现代 GPU 抽象(compute/render)
  • 相对 WebGL 的能力提升
  • 前端应用场景与边界

WebGPU 是新一代浏览器 GPU API:提供现代图形(render pipeline、shader 用 WGSL)与通用计算(compute shader)能力,显式管理资源(buffer/texture/bind group)与命令提交(queue),支持计算着色器并行处理海量数据。相对 WebGL 2 的价值:compute shader(GPU 通用计算,WebGL 无法直接做)、更低的 API 开销(现代驱动模型)、多后端(Vulkan/Metal/D3D12 之上)与更好的多线程(transfer queue);浏览器支持:Chromium 系已默认启用、Safari(WebKit)已实现(2024-2025 持续完善)、Firefox 开发中。

工程价值:图像处理(滤镜、缩放、特征检测在 GPU 并行加速,比 CPU/Canvas 快一个数量级)、游戏渲染(3D 场景、粒子、后处理)、数据可视化(大规模点阵/科学可视化)、ML 推理(WebGPU 作为 WebNN 之外的执行后端);边界:学习曲线陡峭(管线与资源管理复杂)、无回退时需 WebGL2/2D 降级(渐进增强:特性检测 + fallback)、GPU 资源管理需谨慎(内存泄漏与上下文丢失处理)。实践:计算密集用 compute pipeline、渲染用 render pipeline、与 Web Worker(OffscreenCanvas)结合避免阻塞主线程。

考察 WebGPU 的能力定位:compute + render 现代抽象、相对 WebGL 的跃升、场景与降级边界,回答应体现工程落地判断。

#
★★★

17. 长任务切片与主线程释放(scheduler.yield()、Web Worker)

长任务切片与主线程释放(scheduler.yield()、Web Worker)如何实施?

  • 长任务切片的粒度与让出策略
  • Web Worker 迁移的判断与成本
  • 组合策略(交互优先、渲染对齐)

长任务切片的实施:识别 >50ms 任务(Long Tasks/LoAF)→ 在可中断点拆分(循环按批处理:每处理 N 项或每 ~50ms 让出一次)→ 用 scheduler.yield()(或 setTimeout/MessageChannel polyfill)让出主线程 → 输入事件与渲染获得处理机会;关键:让出频率与批大小的平衡(让出过频增加调度开销、过稀仍阻塞)、让出点选择在"无共享状态、可安全中断"处。Web Worker 迁移:把纯计算(解析、加密、图像处理、大数据处理)移入 Worker,主线程只收结果——彻底释放主线程;判断成本:通信(结构化克隆/Transferable 零拷贝)、状态隔离(Worker 无 DOM、不能直接操作 UI)、线程数上限(每 Worker 有内存开销)。

组合策略:交互路径的任务优先切片让出(INP)、可并行的重型计算迁 Worker、与渲染对齐(批处理 DOM 更新放 rAF);分层判断——小任务切片(调度成本低)、大任务迁 Worker(彻底隔离)、中任务按需混合;验证:INP/长任务/LoAF 字段与 Lab 双通道确认主线程占用下降。原则:"能切则切、能移则移、切移结合",按任务性质与成本决策。

考察主线程释放的两种手段:切片(调度层面让出)与 Worker(执行层面迁移),回答应体现按任务规模与性质的决策。

#
★★★

18. 代码分割(route-based、component-level)

代码分割(route-based、component-level)如何实施?有哪些工程要点?

  • 路由级分割与组件级分割的层次
  • 分割粒度与请求成本权衡
  • 与预取、加载态的配合

代码分割把打包产物拆成按需加载的 chunk:route-based——按路由拆分(React.lazy + Suspense、Vue 的动态 import 路由),进入路由才下载对应 chunk,首屏只含当前路由代码,是最常见且收益最大的分割(路由即用户访问单元);component-level——对组件级重依赖(编辑器、图表、视频播放器、弹窗内的大组件)用动态 import 延迟到需要时加载;同时配合库级分割(vendor 拆分、公共 chunk、按需 polyfill)。现代框架的 RSC/并发(React.lazy + Suspense 流式)把分割与加载态统一。

工程要点:粒度权衡——过细分割增加请求数与加载瀑布(HTTP/2 下请求成本降低但仍需权衡)、过粗失去收益,按"访问频率 × 体积"决策;与预取配合(hover/路由可见时 prefetch 目标 chunk 消除导航延迟);加载态(Suspense fallback、骨架屏)设计;首屏 chunk 监控(预算门禁);注意动态 import 的边界(变量路径需静态可分析、命名 chunk 便于缓存与排查)、SSR 下的分割(loadable/动态 import 服务端支持)。本质:按"用户何时需要"组织代码交付。

考察代码分割的层次与方法:路由级/组件级/库级、粒度权衡、预取配合,回答应体现"按访问单元交付"的思想。

#
★★★

19. content-visibility: auto 在长列表渲染跳过的工程价值

content-visibility: auto 在长列表渲染跳过中有哪些工程价值?

  • content-visibility: auto 的渲染跳过机制
  • 与虚拟滚动的组合与差异
  • 占位尺寸与滚动稳定性

content-visibility: auto 让"视口外的元素跳过渲染"(样式计算、布局、绘制被跳过,含 containment 语义),滚动进入视口时才渲染,配合 contain-intrinsic-size 提供占位尺寸(防止未渲染元素高度为 0 导致滚动条跳动)。长列表场景的工程价值:初始渲染只处理视口内元素(首屏时间与内存显著下降)、滚动时按需渲染(滚动性能稳定,不需要虚拟滚动那样的 DOM 复用/测量机制)、实现成本低(纯 CSS 一行声明)。

与虚拟滚动的差异:虚拟滚动"不创建 DOM"(节点数恒定),content-visibility "DOM 存在但跳过渲染"(DOM 数量仍等于全部列表项,DOM 内存仍占);因此超大列表(万级)仍需虚拟化,中等列表(几百到几千)用 content-visibility 即可获得大部分收益且实现简单;组合使用(虚拟化 + auto)可进一步减少滚动时的渲染工作量。边界:Safari 支持较晚(需验证目标浏览器)、占位尺寸不当时滚动跳动、对已渲染元素的更新仍要计算;工程上"千级以下用 auto、万级用虚拟化、两者叠加"是实践指南。

考察 content-visibility 的定位:跳过渲染 vs 虚拟化跳过 DOM,回答应体现两者差异与按规模组合的实践。

#
★★★

20. TanStack Virtual 的虚拟滚动 与 React 19 use()/Server Functions 的协作

TanStack Virtual 的虚拟滚动与 React 19 的 use()/Server Functions 如何协作?

  • React 19 use() 的异步资源读取语义
  • Server Functions 的数据流
  • 虚拟滚动 + 服务端数据的集成模式

React 19 的 use() 允许组件"同步样式"读取异步资源(Promise、context):组件内 use(promise) 挂起渲染直到 resolve,配合 Suspense 无需 useEffect/loading 状态机;Server Functions(RSC 生态)在服务端执行数据操作,数据经序列化传回客户端。协作模式:列表数据的服务端获取用 Server Function 返回 Promise,客户端虚拟滚动组件内 use() 消费(Suspense 边界包裹)、按需加载更多(滚动到底部触发下一批 Server Function 调用)、TanStack Virtual 只负责窗口渲染(count 与测量),数据层由 RSC/Server Functions 供给。

工程价值:虚拟列表的数据获取声明式化(use 内联、无手动状态)、滚动加载与渲染窗口解耦(数据按批到达、渲染按窗口更新)、服务端数据流与客户端交互统一(乐观更新/缓存可与 Server Functions 配合);边界:use() 的 promise 缓存策略(同一 promise 复用、请求去重)、Server Functions 的调用时机(事件/效果中调用)、序列化限制(函数不可传)、虚拟列表的测量不受数据异步影响(占位与 skeleton 在 Suspense fallback)。本质:数据层现代化(RSC)与渲染层虚拟化(TanStack Virtual)正交组合。

考察 React 19 数据原语与虚拟滚动的组合:use() 声明式读取 + Server Functions 数据流 + 窗口渲染,回答应体现分层协作。

#
★★★

21. 合成器动画(transform/opacity)与触发布局/绘制动画的性能差异及图层提升(compositor layer)原理

合成器动画(transform/opacity)与触发布局/绘制的动画性能差异是什么?图层提升的原理是什么?

  • 动画属性的性能分类(布局/绘制/合成)
  • 合成器线程与图层提升机制
  • will-change/translate 的工程实践

CSS 动画按属性开销分三级:触发布局(width/height/top/left、margin 等——需要样式计算与布局重算,成本最高,主线程串行)、触发绘制(color/background/box-shadow——布局不变但重绘像素)、仅合成(transform/opacity——元素作为图层由合成器线程独立处理,主线程不参与每帧)。合成器动画的原理:元素提升为独立合成层(compositor layer),GPU 上直接变换纹理(transform: translate/scale/rotate 与 opacity 渐变),合成器线程(与主线程并行)每帧合成,即使主线程繁忙动画依然流畅。

图层提升:浏览器自动提升(transform 动画中的元素、video/canvas、fixed 元素等),也可用 will-change: transform 提示(谨慎:每个图层占用 GPU 内存与纹理,过量图层(图层爆炸)反而劣化);现代实践用 translate 替代 top/left 动画(不触发布局)、opacity 做显隐(不触发重排)、transform 做位移缩放;验证:DevTools Rendering(Layer borders)查看合成层、性能面板看动画期间主线程是否空闲;边界:合成不降低"每帧内容生成"成本(仍要绘制新内容,如动画中的重排内容)、纹理内存上限。

考察渲染性能的分级认知:布局/绘制/合成三级开销、合成器线程与图层机制,回答应包含 will-change 的谨慎使用。

#
★★

22. 浏览器渲染流水线(Parse HTML、Build DOM、Style、Layout、Paint、Composite)

浏览器渲染流水线(Parse HTML、Build DOM、Style、Layout、Paint、Composite)各阶段做什么?

  • 各阶段的输入输出
  • 流水线的触发(增量/全量)
  • 优化在各阶段的落点

渲染流水线:Parse HTML——HTML 解析器构建 DOM 树(同步脚本阻塞解析,defer/async 缓解);Build CSSOM——CSS 解析构建样式规则树(render-blocking);Style(样式计算)——DOM + CSSOM 匹配计算每个元素的最终样式(选择器复杂度影响);Layout(布局)——按样式计算几何位置与尺寸(盒模型、BFC、依赖内容尺寸;布局属性变化触发全量/增量重排);Paint(绘制)——把元素绘制为位图(绘制指令记录,颜色/阴影/文本);Composite(合成)——分层合成最终帧(合成器线程把图层按顺序合成为屏幕输出)。

触发与优化:任一阶段有依赖变化可能级联(改布局属性 → Layout+Paint+Composite 全链路;改 transform → 仅 Composite);优化落点——减少解析阻塞(脚本策略、HTML 精简)、样式计算(低复杂度选择器、样式内联边界)、布局(避免强制同步布局、批量读写 DOM)、绘制(减少重绘区域、图层化稳定区域)、合成(transform/opacity 动画、图层治理);性能工具按阶段呈现(Performance 面板的 Layout/Paint 条、Lighthouse 审计)。理解流水线是前端性能优化的地基:每个优化手段最终都落在"减少某个阶段的成本"。

考察渲染流水线的整体认知:六阶段职责、级联触发与优化落点,回答应体现"优化 = 减少某阶段成本"的思维。

#
★★

23. Layout Thrashing(强制同步布局)的常见场景与避免的工程实践

Layout Thrashing(强制同步布局)的常见场景有哪些?如何避免?

  • 强制同步布局的机制(读后写)
  • 常见触发场景(循环读布局属性)
  • 避免实践(批处理、rAF、FastDOM)

强制同步布局(layout thrashing)指"读取布局属性"(offsetWidth、clientTop、getBoundingClientRect 等)时若存在"挂起的样式/布局变更"(未完成写入),浏览器被迫先同步执行布局再返回值——读写交替循环中反复强制同步布局,每次强制布局成本 ×N 次循环,造成严重卡顿。常见场景:循环内先写样式再读布局(动画循环中读 offsetTop 定位)、基于布局测量的响应式逻辑(读高度调整其他元素)、JS 动画读取位置计算位移、无限循环列表高度累加(读每项高度)。

避免实践:批处理——读操作集中到写入之前(先读全部所需值再写)、用 requestAnimationFrame 对齐读写(渲染帧内一次性处理)、缓存测量值(只读一次复用)、避免强制同步布局 API 在热路径、FastDOM(把读与写排队合并)或自封装调度;现代替代——用 IntersectionObserver/ResizeObserver 替代滚动/尺寸轮询、CSS 能力(flex/grid、aspect-ratio)减少 JS 测量依赖、用 transform 动画(合成层)避免布局参与。验证:Performance 面板的 Layout 时间占比、Lighthouse 的 layout-shift/布局审计。

考察强制同步布局的机制与治理:读写交替触发、场景识别、批处理与 API 替代,回答应体现"读写分离"的核心原则。

#
★★

24. FLIP(First/Last/Invert/Play)技术的原理与在布局动画(列表重排、共享元素)优化中的应用

FLIP(First/Last/Invert/Play)技术的原理是什么?在布局动画中有哪些应用?

  • FLIP 四步的原理与逆变换
  • 把布局动画转为合成动画的机制
  • 应用场景(列表重排、共享元素过渡)

FLIP(First/Last/Invert/Play)把"昂贵布局动画"转为"合成器动画":First——记录元素初始位置(getBoundingClientRect);Last——记录最终位置(状态变更后);Invert——计算位移差并用 transform 反向位移(元素看起来还在原位);Play——从反向位置过渡到真实位置(transform 动画,合成器处理,主线程零布局)。本质:布局变化"瞬间完成",视觉过渡用 transform 表现——动画期间不触发布局重算,性能从"每帧布局+绘制"降到"纯合成"。

应用:列表重排动画(排序/筛选/增删时元素移动过渡)、共享元素过渡(元素从列表"飞到"详情页——记录两状态做 transform 过渡,替代全屏动画的布局计算)、拖拽排序(占位与位移的流畅过渡);现代补充:View Transitions API 提供了跨状态过渡的原生机制(快照 + 动画),FLIP 仍是自研场景的通用方案;注意:Last 测量需在"写入后读取前"(避免强制同步布局)、逆变换与组合动画(scale 配合 opacity)、边界(动画中布局变化、容器尺寸联动需重算);与 FLIP 配合的库(flip.js)与手写实现要点:测量阶段集中、动画阶段只动 transform/opacity。

考察 FLIP 的机制与应用:四步流程、布局转合成、典型场景,回答应体现"瞬间布局 + transform 过渡"的优化本质。

#
★★

25. View Transitions API 的性能特征(旧视图快照截图机制)与主线程占用、大页面转场的取舍

View Transitions API 的性能特征是什么?旧视图快照机制与主线程占用如何取舍?

  • View Transitions 的快照机制(旧/新视图截图)
  • 主线程与渲染成本(大页面快照)
  • 使用场景与性能治理

View Transitions API(document.startViewTransition)实现页面状态间过渡:浏览器先对"旧视图"截图(快照),更新 DOM 后对"新视图"截图,再用 CSS 动画过渡两者——开发者无需手写 FLIP 的测量与逆变换。性能特征:快照机制把"过渡期间的两个状态"固化为图像,动画由合成器处理(transform/opacity 过渡高效);但成本在于——快照本身是渲染操作(大页面、复杂合成层的快照耗主线程与内存)、过渡期间两帧图像叠加占 GPU 内存;同步过渡阻塞交互(旧快照捕获前主线程忙)。

取舍与治理:同页小范围变化(列表项过渡、弹窗、主题切换)收益大、成本可控;超大页面(长列表、复杂图表)整页转场快照成本高,可限制(view-transition-name 只对关键元素)、分段或禁用(偏好降级:prefers-reduced-motion);跨页转场(MPA 的跨文档转场)依赖浏览器支持与性能;监控:过渡期间主线程任务(Performance)、掉帧;原则:转场是"锦上添花",在内容重、交互密集的页面优先保证主线程空闲,转场降级为无动画。

考察 View Transitions 的成本结构:快照机制高效但大页面成本高、交互密集场景需降级,回答应体现成本意识。

#
★★

26. will-change 属性在 GPU 合成层提升与内存占用的工程取舍

will-change 属性在 GPU 合成层提升与内存占用上有哪些工程取舍?

  • will-change 的提示机制与图层提升
  • 内存与纹理成本(每层 GPU 内存)
  • 使用规范(按需、移除、边界)

will-change: transform/opacity 等向浏览器提示"该元素即将发生合成属性动画",浏览器提前把元素提升为独立合成层(创建纹理),动画开始时避免"临时提升"的顿挫(提升本身有成本,动画首帧才提升会有一次性卡顿);同时它声明了"变化范围"(只承诺的属性变化走合成通道)。成本:每个合成层占用 GPU 内存与纹理带宽,过量提升(大量元素 will-change)造成内存膨胀与合成开销上升,反而劣化(图层爆炸);且提升后元素自身的重绘内容每帧重新上传纹理。

工程取舍与规范:只对"即将动画且动画频繁/持续"的元素使用(如轮播图、拖拽项);动画结束移除 will-change(或不用时删除,避免常驻图层);数量控制(同时提升的图层 < 几十个量级);替代:transform 动画中浏览器通常自动提升(will-change 更多是"提前量"提示),过度依赖会掩盖优化不足;验证:DevTools Layers/Rendering 面板查看图层数量与内存(Memory 面板 GPU 内存)、性能面板确认动画无顿挫。原则:will-change 是"精准提示"而非"性能开关",滥用代价明确。

考察 will-change 的成本结构:提前提升 vs 常驻图层内存,回答应体现"精准、按需、可移除"的使用规范。

#
★★

27. Web Workers 在 CPU 密集型任务(解析、加密)的工程价值

Web Workers 在 CPU 密集型任务(解析、加密)中有哪些工程价值?

  • Worker 的并行执行与主线程隔离
  • 数据传输(结构化克隆、Transferable)
  • 任务划分与回退策略

Web Worker 在独立线程执行 JS(不占用主线程):CPU 密集型任务(大 JSON 解析、加密/哈希、图像处理、数据转换、代码编译)迁移到 Worker 后,主线程保持响应(交互、渲染不受影响),是"主线程释放"的最彻底手段(相比切片是让出、Worker 是移走)。数据传输:postMessage 结构化克隆(深拷贝成本)或 Transferable(ArrayBuffer 零拷贝转移所有权,大数据传输的关键);SharedArrayBuffer 支持共享内存(需跨源隔离头);Worker 数量与内存权衡(每 Worker 一份运行时)。

工程实践:任务划分——纯计算且输入可序列化(parse/encrypt/filter)适合 Worker,依赖 DOM/状态库的复杂交互逻辑不适合(需消息协议化改造);用 Worker 池(任务队列 + 多 Worker 并发)承载高频任务;回退策略——特性检测(不支持/受限环境回退主线程分片执行)、超时与错误处理(terminate 重建);与 OffscreenCanvas 组合(Worker 内绘制、避免纹理跨线程拷贝)。价值总结:把"用户不可感知的计算"移出"用户可感知的主线程",是交互性能的兜底手段。

考察 Worker 的工程价值:并行隔离、Transferable 零拷贝、任务划分与回退,回答应体现"移走 vs 让出"的策略差异。

#
★★

28. OffscreenCanvas 在 Worker 中绘制与主线程性能优化的现代应用

OffscreenCanvas 在 Worker 中绘制与主线程性能优化有哪些现代应用?

  • OffscreenCanvas 的 Worker 内 2D/WebGL 绘制
  • 传输(transferControlToOffscreen)与纹理成本
  • 应用场景(图表、游戏、图像处理)

OffscreenCanvas 允许在 Worker 中创建并绘制 canvas(2D 或 WebGL 上下文):主线程把控制权转交(transferControlToOffscreen 将 HTMLCanvasElement 的控制转移给 Worker 端的 OffscreenCanvas),Worker 内完成绘制后渲染结果直接提交到屏幕(合成器合成),主线程完全不参与绘制逻辑——把"绘制计算"(图表数据渲染、粒子、图像处理、游戏循环)移出主线程,主线程只负责事件与布局。相比"主线程画完传位图"(每帧跨线程传图成本高),OffscreenCanvas 是"Worker 直接画、合成器直接合成",零每帧拷贝。

应用:数据可视化大屏(图表在 Worker 渲染不卡交互)、2D 游戏循环(rAF 在 Worker 的 OffscreenCanvas 上驱动)、图像处理流水线(滤镜/缩放并行)、WebGPU 渲染的 Offscreen 组合;边界:Worker 无 DOM(事件需主线程转发)、上下文数量与 GPU 资源(每个 WebGL 上下文有成本)、部分浏览器 API 差异(字体、CSS 属性不可用需主线程准备);实践:transfer 一次、之后仅同步状态(低频率 message)、监控合成流畅度(掉帧)。价值:把"渲染计算"与"交互线程"彻底分离,是大规模可视化与游戏的性能基石。

考察 OffscreenCanvas 的机制:Worker 内直接绘制并合成、零拷贝传输、典型场景,回答应体现"绘制移出主线程"的核心。

#
★★

29. requestIdleCallback 与 scheduler.yield() 在低优先级任务的工程应用

requestIdleCallback 与 scheduler.yield() 在低优先级任务中有哪些工程应用?

  • requestIdleCallback 的空闲时段机制
  • scheduler.yield 的优先级与中断语义
  • 低优先级任务的调度与取舍

requestIdleCallback(fn, { timeout }) 在浏览器"空闲时段"执行回调(空闲时间片由浏览器计算,deadline 提供剩余时间,可超时兜底):适合低优先级、可随时中断的任务(预计算、日志上报、非关键数据整理、后台预取分析)——不抢占交互与渲染。scheduler.yield() 是让出原语(配合 Scheduler API 的优先级与 AbortSignal):当前任务主动让出,让更高优先级(交互)先跑;两者组合可构建"空闲时做、忙时让"的调度——把任务包在 requestIdleCallback 中,内部循环里用 scheduler.yield() 进一步让出。

工程应用与取舍:上报批处理(空闲时合并发送)、渲染后的非关键计算(图表数据预处理)、页面交互间隙的准备工作(预创建节点);注意:requestIdleCallback 的"空闲"定义依赖浏览器负载(高负载时可能长期不触发,需 timeout 兜底)、回调内不能做长时间任务(会阻塞之后的渲染)、兼容性(可 polyfill 或用 MessageChannel 近似);scheduler.yield 需要 Chromium 系(可 polyfill);低优先级任务的原则:"可延后、可中断、超时兜底",与高优先级(交互)显式分层。

考察低优先级调度的两个 API:空闲时段执行与主动让出,回答应体现"可延后可中断 + 超时兜底"的原则。

#
★★

30. 防抖(debounce)与节流(throttle)在高频事件(scroll、resize)

防抖(debounce)与节流(throttle)在高频事件(scroll、resize)中如何应用?

  • debounce 与 throttle 的语义差异
  • 高频事件的场景选择(搜索、滚动、拖拽)
  • 实现要点与边界(首次触发、尾调用)

debounce(防抖)把连续触发合并为"最后一次"执行:停止触发后等待 N 毫秒才执行,适合"以最终状态为准"的场景(搜索输入请求、resize 结束后的重排、表单自动保存);throttle(节流)固定频率执行(如每 100ms 至多一次),适合"过程需要持续响应但可降频"的场景(滚动位置更新、拖拽位移、滚动懒加载判断、进度条)。高频事件(scroll/resize/mousemove 每帧多次触发)直接绑定处理器会造成大量布局读取与重渲染。

实现要点与边界:防抖有"只执行尾部"的局限(持续操作永远不执行)——需首次执行(leading)或 maxWait 兜底(如搜索防抖 300ms + maxWait 1s 保证有响应);节流有"固定节奏"与"首尾是否执行"选项(leading/trailing);现代替代——滚动/尺寸用 IntersectionObserver/ResizeObserver(浏览器级异步批量,无需手动节流)、动画用 rAF(帧对齐);注意节流/防抖的实现质量(setTimeout 清理、时间戳比较)与"延迟导致的体验"(搜索延迟反馈需 loading 态);验证:高频场景的处理器调用次数与主线程占用。

考察防抖节流的精确语义与场景匹配:尾触发 vs 定频、leading/maxWait 边界、Observer 替代,回答应体现"按事件语义选型"。

#
★★

31. requestAnimationFrame 在动画与渲染同步的工程实践

requestAnimationFrame 在动画与渲染同步中有哪些工程实践?

  • rAF 的帧同步机制(绘制前回调)
  • 动画循环与时间步进(delta 时间)
  • 与节流、批处理的配合

requestAnimationFrame 的回调在"下一帧渲染前"执行:与显示器刷新同步(60Hz/120Hz),浏览器保证每帧至多一次回调、页面不可见时暂停(省电)——它是动画与"渲染前准备"的标准时机。动画实践:动画循环用 rAF 驱动(递归调用),用时间戳计算 delta(帧间隔)做时间步进(避免"每帧固定增量"在低帧率下变速)、暂停/恢复管理、与 CSS 动画/WAAPI 的选择(JS 动画适合逻辑驱动:进度、缓动自定义、暂停恢复复杂场景)。

配合实践:rAF 作为"批处理时机"——多处在同一帧内的 DOM 写入合并到 rAF 回调(避免布局抖动)、配合节流(滚动处理中读值用 rAF 对齐而非每次事件);rAF 回调内避免长任务(会阻塞渲染,超时帧自动跳过);后台节流(页面隐藏时 rAF 暂停,计时逻辑需用真实时间);与 scheduler.yield 的区别:rAF 是"渲染前对齐"、yield 是"让出主线程";调试:Performance 面板看 rAF 回调耗时与帧率。原则:动画与帧对齐、批处理与渲染对齐、时间用时间戳。

考察 rAF 的机制与实践:帧同步、delta 时间步进、批处理时机,回答应体现"与渲染对齐"的核心理念。

#
★★

32. Main Thread 任务分块(Chunking)

Main Thread 任务分块(Chunking)的原理与实践是什么?

  • 任务分块的原理(时间片让出)
  • 分块粒度与让出时机
  • 与渲染/交互的配合

主线程任务分块(chunking)把超过 50ms 的连续工作拆成多个小块,块与块之间让出主线程(yield),让浏览器有机会处理输入事件与渲染帧——核心原理是"时间分片 + 让出":单块执行时间控制在帧预算内(如每块 ≤ 10-20ms,或每块后检查 elapsed 决定是否让出)。实践形态:遍历类任务按批处理(每 N 项一批)、计算类任务按时间预算分批(每批处理到 deadline)、渲染类任务批量收集后统一更新。

实现:用 scheduler.yield()/setTimeout/MessageChannel 做让出点;配合帧对齐(让出后等 rAF 确认渲染完成再继续);粒度权衡——块太小调度开销占比高、块太大仍会阻塞;与"优先级"结合(交互期间暂停低优先级分块);工具:React 并发(自动分块渲染)、框架的并发特性本质也是分块调度;验证:Long Tasks 消失/变短(Performance 面板、LoAF)、INP 改善。原则:让"长任务"变成"短任务序列 + 渲染间隙",保障输入与绘制的机会。

考察任务分块的机制与实践:时间预算批处理、让出时机、粒度权衡,回答应体现"分块 + 让出 + 帧对齐"的组合。

#
★★

33. JSON.parse 在 Worker 中处理大型 JSON 数据的工程实践

JSON.parse 在 Worker 中处理大型 JSON 数据有哪些工程实践?

  • 大 JSON 解析的主线程阻塞
  • 数据到达与传输的优化(流式、Transferable)
  • Worker 化解析的取舍

大型 JSON(数 MB)在主线程 JSON.parse 会产生长任务(解析是 CPU 密集),阻塞交互与渲染;工程方案:解析移入 Worker——主线程把 JSON 文本传给 Worker(字符串传递),Worker 内 parse 后把结果传回(结构化克隆回传大对象仍有成本,可用"Worker 内直接消费"模式:解析+处理都在 Worker,主线程只收处理结果/视图数据)。传输优化:fetch 返回的文本本身在内存中(response.text() 拷贝),大数据流式读取(ReadableStream 分块、JSON.parse 不支持增量,可用 streaming parser 如 jsonparse 或分块处理)、Transferable 仅适用二进制(ArrayBuffer 零拷贝,但 JSON 文本是字符串)。

工程实践:阈值判断(>1MB 才走 Worker,小数据直接解析省通信)、解析与业务处理同在 Worker(避免回传大对象)、用流式解析处理超大 JSON(分块 parse 或流式 AST)、进度与取消(分块间 yield、AbortSignal);边界:Worker 通信是异步消息(代码结构需改造)、回传结果若仍是大对象,主线程"接收+使用"仍有成本(数据分页/投影化);验证:主线程长任务消失(Performance)、交互无卡顿。本质:把"解析耗时"从交互路径移到后台线程。

考察大 JSON 处理的工程化:Worker 化解析、回传成本、流式处理,回答应体现"解析 + 消费都在 Worker"的完整模式。

#
★★

34. Streams API 在流式响应与渐进渲染的工程应用

Streams API 在流式响应与渐进渲染中有哪些工程应用?

  • 流式读取(fetch body 分块、ReadableStream)
  • 渐进渲染(首块先绘、LLM 流式输出)
  • 背压与取消控制

Streams API(ReadableStream/WritableStream/TransformStream)允许"分块消费"数据而非等全量:fetch 响应的 body 是 ReadableStream,可用 reader.read() 逐块读取——首块到达即可处理(TTFB 后立即渐进),适合大响应(列表分页数据、日志、文件)与"边到边用"(流式 UI 更新)。渐进渲染应用:SSR 流式输出(React 的 renderToPipeableStream、Vue 的 pipeToNodeWritable——HTML 分块下发、首屏先绘)、LLM 聊天流式输出(SSE/分块文本逐字渲染)、大数据表格流式灌入(边加载边渲染窗口)。组合:TransformStream 做处理管道(解码、解析、转换在流中完成,如 NDJSON 逐行 parse)。

工程要点:背压(消费者慢时流暂停,避免内存堆积——reader.read 的自然拉模式即背压)、取消(reader.cancel() 中止下载)、错误传播(流中异常终止);对 JSON 需流式解析器(非 JSON.parse 全量)、UI 侧批处理(每块渲染合并到 rAF);边界:浏览器支持广泛(现代浏览器均支持)、服务端需支持流式(HTTP/1.1 chunked、SSE);验证:首块时间(TTFB → 首 content)、渐进渲染感知。本质:从"全量到达再消费"到"边到达边消费",重塑大数据的体验。

考察流式处理的机制与应用:分块消费、渐进渲染、背压与取消,回答应体现"边到边用"的数据处理范式。

#
★★

35. SpeedCurve、Calibre 在合成监控的持续集成应用

SpeedCurve、Calibre 等合成监控平台在持续集成中有哪些应用?

  • 合成监控平台的能力(趋势、对比、告警)
  • CI 集成(部署后自动测试、回归对比)
  • 与 RUM/预算的协同

SpeedCurve/Calibre 是商业合成监控平台:云端按计划对页面运行性能测试(Lighthouse/WebPageTest 引擎),产出指标趋势(时间序列)、版本对比(deploy 标记前后差异)、预算告警(超阈值通知 Slack/邮件)与回归定位(哪次部署引入劣化)。持续集成应用:部署流水线触发"发布后监控"(每环境 URL 组自动测试)、PR/发布对比(当前分支 vs 基线分支的指标 diff)、性能预算在平台层强制执行(与 CI 状态联动);平台还提供多地点、多浏览器、真实设备(部分)测试矩阵。

协同:Lab 平台(合成)管"每次变更的趋势与回归",RUM(自建/第三方)管"真实用户分布",预算门禁(LHCI/size-limit 在 CI 内)管"合入前阻断"——三层分工:合入前门禁(快)、发布后趋势(平台)、生产分布(RUM);平台优势是"开箱即用 + 历史对比 + 告警编排",代价是订阅成本与云端环境黑盒;自建替代(GitHub Actions + LHCI + 存储)成本低但需自建趋势与告警。选型:按团队规模与监控成熟度,用平台快速建立基线,再逐步接入 RUM 与预算门禁。

考察合成监控平台在 CI 的定位:趋势/对比/告警、发布后验证、与预算及 RUM 的分层,回答应体现三层监控架构。

#
★★

36. 性能监控指标的采集成本与采样策略,PerformanceObserver buffer 上限与批量上报的工程取舍

性能监控指标的采集成本与采样策略如何权衡?PerformanceObserver buffer 上限与批量上报如何处理?

  • 采集成本(观察器数量、回调频率、内存)
  • 采样策略(全量 vs 抽样、维度分层)
  • buffer 上限与批量上报的工程处理

性能采集的成本:观察器回调在主线程执行(高频条目如 resource/longtask 回调开销)、性能条目缓冲区内存(条目堆积)、上报网络开销(每指标一条请求)。采样策略:全量采集(关键指标 LCP/INP/CLS 每会话一次,成本可接受)+ 抽样采集(高频/大维度数据按比例或按条件采样:如 longtask 采样 10%、按页面/设备分层抽样)+ 聚合上报(浏览器端聚合(分位数/计数)后批量发送);buffer 上限处理:entry buffer 有上限(约 150 条/类型,可扩容 resource),观察器需及时消费(回调内处理或转存)、用 performance.setResourceTimingBufferSize 扩容、监控 dropped entries(缓冲区溢出丢条目:通过对比计数器识别缺口)。

批量上报:指标在浏览器端收集队列,定时(如 10s 批)或空闲(requestIdleCallback)合并发送(sendBeacon/fetch keepalive 保证页面卸载不丢);对 INP 等"会话级"指标最后交互延迟上报(页面隐藏时补齐)。取舍原则:采集成本与数据价值的匹配——核心指标全量、辅助指标抽样、明细按需(问题页面才开 attribution),保证监控本身不成为性能负担。

考察性能采集的工程经济学:成本构成、分层采样、buffer 与批量上报,回答应体现"监控不成为负担"的原则。

#
★★

37. RUM 与合成监控的协同,抽样、维度关联与告警阈值

RUM 与合成监控如何协同?抽样、维度关联与告警阈值如何设计?

  • 双通道数据的互补关系
  • 抽样与维度对齐
  • 告警阈值的分层设计

RUM(真实分布)与合成(可复现基线)的协同:合成负责"变更检测"(部署前后对比、回归定位)、RUM 负责"真实评估"(用户分维度体验、业务影响),两者数据互补不互斥。协同设计:抽样——RUM 按流量比例抽样(全量在低流量站点可直接全采、高流量按 1% 采样保证统计量)、合成按环境频率(每部署一次);维度关联——两通道的公共维度(页面、设备类、地域、版本)统一口径,RUM 发现问题后回合成复现定位(RUM 报"移动端 LCP p75 劣化" → 合成按移动设备复现归因);告警阈值——分层设计:合成阈值(固定环境、稳定,可用严格阈值 + 多次运行中位数防抖)、RUM 阈值(真实分布波动大,用 p75 + 环比(相对上一周期变化率)触发,避免绝对阈值误报)、分级告警(warning/error、按维度(地域/版本)隔离告警避免全局轰炸)。

实践流程:RUM 告警 → 定位影响面(维度下钻)→ 合成复现与归因(Lab 诊断)→ 修复 → 双通道验证(RUM p75 回落、合成指标达标);工具:RUM 平台(自建/第三方)、合成平台(LHCI/SpeedCurve)、告警编排(阈值 + 抑制 + 升级)。本质:合成"快而准地测变更"、RUM"真而全地测体验",协同闭环。

考察双通道监控的协同设计:抽样口径、维度对齐、阈值分层与"RUM 发现-合成定位"流程。

#
★★

38. 边缘渲染(Edge Functions)对 TTFB 的影响

边缘渲染(Edge Functions)对 TTFB 有哪些影响?

  • Edge Functions 的执行位置(CDN 节点)
  • 对 TTFB 的缩短机制与边界
  • 边缘渲染的适用与限制

Edge Functions(Vercel Edge、Cloudflare Workers、Netlify Edge)在 CDN 边缘节点执行轻量逻辑(与用户地理接近):相比"请求回源站处理再返回"(全球往返延迟),边缘执行把处理与响应都放在最近节点,TTFB 显著缩短(RTT 从"用户↔源站"降为"用户↔边缘");适合轻逻辑(鉴权重定向、A/B 分流、个性化头部、边缘缓存决策、静态页动态包装);与 SSR 的差异:边缘运行时受限(无 Node 全 API、无长任务)、执行时间预算(平台限制如 10-50ms 计费单位)、数据访问需走边缘缓存/边缘数据库。

影响与边界:TTFB 收益的前提是"逻辑能完全在边缘完成"——需要回源数据库/服务的请求 TTFB 仍受源站拖累(可用边缘缓存、SWR 减少回源);边缘渲染的 HTML 可配合 103 Early Hints 进一步提速;限制:运行时能力(无 fs/无重 CPU)、函数冷启动(部分平台)、调试与本地模拟差异、成本;工程判断:边缘渲染适用于"逻辑轻、就近收益大"的页面(登录态判断、地域化、静态+个性化),重型 SSR 仍用源站 + CDN 缓存组合;验证:TTFB 分地域对比(边缘部署前后)。本质:TTFB = 就近处理 + 减少回源,Edge 是"就近"的现代实现。

考察边缘渲染对 TTFB 的机制:就近执行缩短 RTT、回源仍拖累、运行时限制,回答应体现适用判断。

#
★★

39. 长会话 SPA 的内存增长基线建立方法与回归检测(多次快照对比、强制 GC 后取样)

长会话 SPA 的内存增长基线如何建立?回归检测怎么做?

  • 长会话内存问题的特殊性(累积而非单次)
  • 基线建立(强制 GC、多次快照对比)
  • 回归检测(自动化、阈值、触发条件)

长会话 SPA(编辑器、后台、IM)的内存问题具有累积性:单次操作不泄漏,但反复操作(打开关闭面板、切换列表、长会话路由)后内存持续增长,最终卡顿崩溃;因此基线建立以"稳定态测量"为准:固定测试场景(一组代表性操作序列),强制 GC(--js-flags="--expose-gc" 的 gc() 或 DevTools Collect Garbage)后取样堆内存,多次重复(如 5 次循环后)对比——若每次循环后"强制 GC 后的残留内存"递增,判定存在泄漏/累积;基线 = 无泄漏版本在该场景下的残留内存曲线(每次循环净增应趋近 0 或低于阈值)。

回归检测自动化:Playwright/CDP 驱动(注入 gc 采样、Performance.getMetrics 的 JSHeapUsedSize)、每版本 CI 运行场景集、指标(循环后堆增量、增长斜率)与阈值(如每次循环净增 < 1MB、斜率 < X)断言;触发条件(内存超限、增长率异常)联动告警;注意测量噪声(GC 不彻底用两次 GC、JIT 缓存影响冷热态、采样时机统一);治理:泄漏修复后更新基线(防止旧基线误报)、场景集随功能演进维护。本质:长会话性能治理的关键是"趋势测量"——基线 + 斜率而非单点数值。

考察长会话内存治理的方法:稳定态基线、强制 GC 取样、回归阈值与自动化,回答应体现"趋势检测"的核心。

#
★★

40. 大闭包与字符串拼接的内存影响,以及流式处理(Streams)替代全量缓冲的取舍

大闭包与字符串拼接的内存影响是什么?流式处理(Streams)替代全量缓冲如何取舍?

  • 闭包捕获大对象的作用域保持
  • 字符串拼接的中间副本问题
  • 流式 vs 全量缓冲的取舍

大闭包的内存影响:闭包保持对外层作用域变量的引用,若外部函数持有大对象(大数组、长字符串、DOM 引用)且内部函数被长期持有(事件回调、定时器、全局注册),整个作用域链无法释放——"闭包看起来很小、捕获的东西很大";特征:长会话累积、快照中 Closure 引用链显示大对象。字符串拼接的影响:不可变字符串的 + 拼接会产生中间副本(每次拼接创建新字符串,大循环拼接产生 O(n²) 拷贝与大量临时对象),现代引擎对循环拼接有优化(rope/cons 字符串)但复杂场景仍建议 join/数组收集、模板串按需;大数据处理中全量缓冲(先收集完整再处理)使峰值内存翻倍(原始 + 处理后)。

流式替代取舍:Streams 分块处理避免全量驻留(边读边处理、峰值内存恒定),适合大文件解析、日志处理、大响应转换;取舍——流式复杂度高(状态机处理、背压)、难以"回头看"(全量数据才能随机访问的场景仍需缓冲)、吞吐场景(快速小数据)流式收益小;工程判断:数据规模大且可顺序处理 → 流式;需随机访问/聚合多次 → 缓冲 + 内存预算;配合 Worker(处理在后台线程)减少主线程压力。本质:内存治理 = 及时释放(闭包/引用)+ 减少驻留(流式/复用)。

考察内存的两类陷阱与对策:闭包/拼接的累积与中间副本、流式降峰值,回答应体现"驻留最小化"的内存思维。

#
★★

41. Map/Set 缓存无界增长的内存风险与 LRU/容量上限治理

Map/Set 缓存无界增长的内存风险是什么?LRU/容量上限如何治理?

  • 缓存无界增长的累积机制
  • LRU 策略与容量上限实现
  • 缓存治理(过期、淘汰、监控)

用 Map/Set 做缓存(查询结果、已处理数据、去重集合)时若只增不减:缓存条目随时间无界增长,最终耗尽内存(长会话明显);尤其"引用大对象"的缓存(缓存整个响应/组件数据),条目数与内存呈线性增长且不可回收(Map 强引用)。治理手段:容量上限——缓存写入时检查 size,超限触发淘汰(最旧/最不常用);LRU 策略——记录访问顺序(Map 的迭代顺序 + 每次访问 delete 再 set 提升到末尾实现"最近使用在前/淘汰最久未用"),或使用现成 LRU 库(lru-cache,支持 maxSize 与 ttl);过期——TTL 定时清理(结合访问时惰性过期);弱引用兜底——WeakMap/WeakRef 让 GC 可回收无外部引用的缓存项(适合"可重建"缓存)。

工程实践:缓存统一封装(提供 put/get/evict 与统计)、容量与 TTL 按业务设定(条目数与内存预算)、淘汰事件监控(命中率、淘汰率)与内存基线关联;边界:LRU 保证"有界",不保证"数据新鲜"(需 TTL 配合)、命中率与容量权衡(容量小淘汰频繁);监控:缓存内存占比、命中率趋势、无界增长告警。本质:缓存是"内存换速度"的工程,必须显式治理边界。

考察缓存内存治理:无界增长机制、LRU/容量/TTL/弱引用手段,回答应体现"缓存必须有界"的工程纪律。

#

42. WebAssembly 在浏览器端计算密集型任务(图像处理、加密)

WebAssembly 在浏览器端计算密集型任务(图像处理、加密)中有哪些应用?

  • WASM 的执行模型与性能特性
  • 与 JS 的性能差异及适用场景
  • 工程集成(加载、线性内存、降级)

WebAssembly 是浏览器端的字节码执行格式:接近原生性能(线性内存 + 固定类型,规避 JS 的动态优化不确定性),适合 CPU 密集型任务:图像处理(滤镜、缩放、编解码——如 wasm 版图像库)、加密/哈希(AES/SHA 高性能实现)、音视频处理(解码)、数据压缩、科学计算、游戏逻辑;相对 JS 的优势是"性能可预测"(JIT 波动小)与"跨语言复用"(C/C++/Rust 编译到 wasm 的成熟生态);相对原生局限:无 DOM 直接访问(需与 JS 桥接)、线程(WASM threads 需 SharedArrayBuffer 与跨源隔离头)、调用边界开销。

工程集成:按需加载(动态 import wasm 模块 + 编译缓存)、线性内存管理(显式分配/释放、避免泄漏)、与 Worker 组合(WASM 计算放 Worker 主线程零负担)、降级策略(不支持环境回退 JS 实现:特性检测 + 双实现)、体积治理(wasm 产物大小、优化级别);性能验证:微基准对比(wasm vs JS 实现)、真实场景(大图处理耗时);适用判断:算法确定、数据量大、性能敏感(图像/加密/压缩)值得 wasm;轻量逻辑 JS 足够(wasm 有加载与桥接成本)。

考察 WASM 的应用判断:性能特性、场景适配、集成与降级,回答应体现"性能可预测 + 场景匹配"的取舍。

#

43. 真实用户监控(RUM)与合成监控的协同

真实用户监控(RUM)与合成监控如何协同?

  • 两通道的能力互补
  • 发现-复现-定位-验证流程
  • 协同的工程组织

RUM 与合成监控的互补:RUM 覆盖真实用户(设备、网络、路径、交互的真实分布,能发现"只有真实环境才有的问题")但不可复现、无诊断;合成监控覆盖受控环境(可复现、可对比、有诊断审计)但代表"模拟环境"。协同流程:RUM 发现问题(指标劣化告警)→ 维度下钻定位影响面(设备/地区/版本)→ 合成复现(按受影响维度配置环境)并诊断(Lighthouse/性能面板归因)→ 修复 → 双通道验证(RUM p75 回落 + 合成指标达标);合成也能"预防"(门禁与回归)减少 RUM 层面的劣化。

工程组织:统一指标口径(两通道用同一指标定义与阈值语义)、共享维度体系(页面、设备类、地域、版本)、数据串联(RUM 问题事件与合成测试运行关联)、告警分级(RUM 真实劣化告警 + 合成回归告警分开处理避免噪音);工具链:RUM 平台 + LHCI/SpeedCurve + 告警编排。本质:合成"快而准地验证变更"、RUM"真而全地评估体验",双通道闭环覆盖"开发-发布-生产"全周期。

考察双通道协同的完整机制:互补能力、发现-定位闭环、统一口径,回答应体现"合成防回归、RUM 定真实"。

#

44. reportWebVitals 与 Google Analytics/BigQuery 集成的工程价值

reportWebVitals 与 Google Analytics/BigQuery 集成的工程价值是什么?

  • reportWebVitals 回调的上报能力
  • GA4 事件与 BigQuery 的流转
  • 分析价值(维度关联、分布聚合)

reportWebVitals(CRA/Next 模板自带)是 web-vitals 库的接入回调:onLCP/onINP/onCLS 等指标完成后把数据(名称、值、评级、页面信息)传给分析平台——GA4 用 gtag 事件(如 web_vitals_store,维度含 metric_value、metric_rating),或通过 GA4 360 的 BigQuery export 把事件流同步到 BigQuery;工程价值:无需自建数据管道即可获得"性能 + 业务"的统一分析底座:BigQuery 中可按维度(页面、设备、地区、来源、用户属性)聚合性能分布、与业务事件(转化、留存)关联分析、自定义 SQL 做 p75/p90 计算与告警。

实践要点:上报时机(指标稳定后/页面隐藏时)、维度扩展(自定义维度:路由、版本、实验组)、采样控制(GA4 默认抽样 vs 全量阈值)、隐私(指标不含 PII);BigQuery 的价值是"可查询的历史明细"——比 GA 面板更灵活(关联业务表、建模分析);替代方案:自建 RUM(数据主权但需管道);选型:无数据团队/快速启动用 GA4+BigQuery 组合,数据敏感或深度定制用自建。本质:把性能指标接入既有分析体系,让"性能影响业务"可量化。

考察性能数据与分析平台的集成:reportWebVitals 上报、GA4/BigQuery 流转、维度与业务关联价值。

#

45. addEventListener/removeEventListener 不配对导致监听器泄漏的排查(EventListeners 面板/heap 中 Listener 对象)

addEventListener/removeEventListener 不配对导致监听器泄漏如何排查?

  • 监听器泄漏的机制(元素被引用、回调存活)
  • DevTools EventListeners 面板的使用
  • heap 中 Listener 对象的定位

监听器泄漏的机制:addEventListener 后未 removeEventListener(或移除的是不同引用——匿名函数无法按引用移除、箭头函数每次新建),导致:元素被监听器系统引用(元素无法回收 → detached DOM 泄漏)、回调闭包引用的大对象长期存活。排查入口:DevTools Elements 面板选中元素查看 Event Listeners 面板——列出该元素所有监听器(含框架/库代理注册的),检查"移除后仍存在的监听器";Performance 面板记录后查看 listener 数量增长;Memory 面板的 heap snapshot——Detached 节点查看其 listener 引用链(Retaining Tree 中的 listener 对象 → 回调 → 闭包作用域)。

实践:排查流程——复现操作(反复创建销毁)→ 观察 EventListeners 面板冗余监听器或快照中 Detached 增长 → 沿引用链定位注册点 → 修复(配对移除:保存引用或用 AbortController(signal 统一清理)、框架生命周期清理(React useEffect cleanup、Vue onUnmounted));预防:统一封装事件绑定工具(自动配对)、页面卸载清理全局监听、监控(快照对比中 Listener 增长);注意"一次注册多处引用"(同一回调注册多次需各自移除)。本质:监听器是"元素 ↔ 回调"的双向引用,泄漏排查即追踪引用链两端。

考察监听器泄漏的排查方法论:机制、EventListeners 面板、快照引用链,回答应体现"配对清理 + 引用链定位"。

#

46. iframe 未销毁与全局变量累积的内存影响及卸载时的清理策略

iframe 未销毁与全局变量累积的内存影响是什么?卸载时如何清理?

  • iframe 的资源成本(独立渲染进程/文档)
  • 全局变量累积的机制
  • 卸载清理策略(移除、断引用、事件清理)

iframe 的内存成本:每个 iframe 是独立文档(+ 可能独立渲染进程/上下文),含自己的 JS 堆、DOM、样式与缓存资源——未销毁(从 DOM 移除但仍有 JS 引用:事件监听、闭包引用其 contentWindow、定时器仍在跑)的 iframe 占用大量内存且不回收;常见于动态嵌入(广告、三方组件、富文本编辑器)反复挂载卸载后未彻底清理。全局变量累积:window 上的全局对象/缓存(挂载到 window 的组件实例、全局数组/对象不断追加、module 级变量被赋值大对象)随会话增长——全局引用使 GC 永远无法回收,长会话内存持续上升。

卸载清理策略:显式移除(iframe.remove() + 清空 src(释放文档与资源))、断开引用(引用 iframe/contentWindow 的变量置 null、移除事件监听)、销毁内部状态(编辑器/三方 SDK 的 destroy/清理方法)、定时器清理、对全局缓存用容量上限与显式清空(退出登录/会话重置时重置全局状态);iframe 场景还可用"容器复用"(不反复创建销毁,而是复用并重置内容);监控:长会话堆快照中 iframe 相关对象/Detached 数量。本质:卸载 = 移除 + 断引用 + 清状态三步,防止"看不见的引用"留住资源。

考察 iframe 与全局状态的清理:资源成本、引用断开、卸载三步清理,回答应体现"移除+断引用+清状态"的完整策略。

#

47. 内存指标与 GC 日志在长任务、卡顿根因分析中的协作

内存指标与 GC 日志如何在长任务、卡顿根因分析中协作?

  • GC 暂停与卡顿的关联
  • GC 日志/事件的读取(DevTools、CDP)
  • 内存与任务的联合分析流程

GC 是卡顿的隐藏根因:堆增长后 Major GC(标记-清除/压缩)耗时显著(几十到几百 ms),表现为"偶发长任务"(卡顿周期性出现、与分配模式相关);联合分析流程:先确认卡顿是否与 GC 相关——Performance 面板中长任务上方的 GC 标记(紫色条)、DevTools 的 Rendering/内存面板(GC 事件时间线)、CDP 的 HeapProfiler 事件;再关联内存增长——分配时间线/堆快照看堆使用趋势(GC 频繁 = 分配压力大、GC 暂停长 = 存活对象多/堆大)。指标层面:JSHeapUsedSize(Performance.getMetrics)随时间曲线、GC 频率与时长、长任务中的 GC 占比。

协作分析:卡顿根因分两类——业务长任务(脚本执行)直接可见(函数栈),GC 长任务需结合"堆增长源"定位(谁在分配/谁在累积);治理:GC 相关卡顿的优化是"减少存活对象与分配压力"(复用、缓存有界、避免大对象反复创建、及时断引用),而非直接调 GC 参数;长会话监控:内存基线(强制 GC 后取样)与 GC 事件关联告警(GC 暂停超阈值)。本质:内存与性能是同一枚硬币的两面——卡顿定位必须同时看"任务构成"与"内存行为"。

考察 GC 与卡顿的联合分析:GC 暂停识别、内存增长归因、双维度治理,回答应体现"任务 + 内存"协同定位。