如何用浏览器开发者工具 (DevTools) 定位效能相关问题?
2025年7月3日
在前端工程师的工作中,经常会遇到各种问题,可能是某段程式码运作不如预期,也可能是某个功能虽然正常,但是效能过慢。当遇到这些问题时,能否快速定位问题,并快速解决问题,会是区分一个前端工程师资深与否的分水岭。
先前我们在 《什么是可观测性(Observability)? 为什么需要可观测性?》 一文谈到,软体工程师在解决问题的过程中,需要有工具协助直指问题核心,才不会在茫茫大海的可能性中,花费不必要的时间去猜问题是什么。
这个概念在前端工程领域也是一样的,当前端工程师要解某个 bug 时,也会需要搭配对应的工具来找问题;而对前端工程师来说,最需要熟知的工具,莫过于浏览器的开发者工具。因此,在接下来几期的前端主题文中,我们会来谈如何善用开发者工具,定位各类问题,这期会先从效能相关的问题开始谈。
为什么要懂浏览器的开发者工具?
如同在用任何工具之前,比起用工具本身,了解「为什么」要用该工具是更重要的。在使用浏览器的开发者工具前,也需要先了解为什么要懂浏览器的开发者工具,以下让我们用几个情境来说明。
假如某个前端工程师,要实作一个对接后端 API 的功能,让使用者可以新增一笔资料,新增完后要马上显示新资料在画面上;但是在实际对接后,发现功能并不如预期运作。
在这个状况下,如果不用额外的工具,该前端工程师能做的,是在自己的程式码中找问题在哪,然后去对照后端提供的 API 串接规格,来看是否有接对该 API。或者是实际的用 cURL 来呼叫 API 确保 API 是可以呼叫的,以及传入的引数与收到的回覆都如预期。
但上面这个做法显然会花很多时间,一个更快速找问题的方式,是打开浏览器的开发者工具,开启 Network 面板,然后实际查看每一支 API 的呼叫,借此快速定位是否是 API 对接的问题。假如发现 API 对接没问题,就能快速回到程式码查看其他部分,这时也可以透过埋 console
的方法,然后搭配开发者工具的 Console 面板,来查看不同的输入与输出结果。这种做法,会比上面的快速有效许多。
再来看看另一个例子,假如今天新功能在测试阶段,产品端回报说页面的加载异常的慢。如果在没有额外工具的状况下,只能尽可能地用上自己所知道的页面效能优化的手段,但这种做法不是对症下药,所以可能要试很多不同手段,才能有效优化。然而,如果有开发者工具,就可以开启 Performance 面板,透过不同的方式,来快速定位加载慢是慢在哪。
希望透过上面两个例子,读者们有比较具体地感受到,当有了浏览器的开发者工具,前端工程师能更轻易地找出问题,这也是为什么前端工程师需要懂如何善用开发者工具。
如何定位效能 (Performance) 相关问题?
首先,让我们聚焦在 Performance 这个面板上,来谈可以如何透过这个面板快速定位效能相关的问题。
查看浏览器生命周期事件
从使用者进到一个页面之后,浏览器会也不同的生命周期事件,在 《请说明 DOMContentLoaded, load, beforeunload, unload 的触发时机》 一文中,我们有介绍过这个面试中经常会考的问题。然而,比起去死记硬背这个面试题,用 Performance 面板中所展示的来理解会更有效。

假如打开 Perfomance 面板,然后点击重新整理与录制键,接着操作页面,就可以出现类似上面这一张图 (上面这张是拿 E+ 用的 Notion 页面为例)。在图中我们自己加了三个箭头,分别是 DCL (DOMContentLoaded)、L (Load) 以及 LCP 的时间点。
可以看到,假如今天用 Performance 面板,就能很清楚地看到这些时间点,进而能够去比较这些时间点是否有如预期发生。这能够给前端工程师一个清楚的指引,来决定要不要花时间优化某些东西。
举例来说,如果 DOMContentLoaded
太晚才发生,意味着浏览器花太多时间载入并解析 HTML 文件,如果要优化这块,例如压缩 HTML 档案大小,移除不必要的注解和空白字符,或把阻塞 DOM 解析的 JavaScript 移到页面底部,或用 defer
让脚本在 DOM 解析完成后才执行 (在 《<script> 的 async 与 defer 有什么不同?》 一文有详谈)。
查看每个函式所花费的时间
在看 Performance 面板时,我们也可以精细地去看每个函式所执行的时间。在这样的一张火焰图 (flame chart) 中,我们可以看到每一个函式呼叫所需的时间。

可以看到,在每一个函式被呼叫后,可能该函式底下又呼叫了其他函式,然后一层层呼叫下去。在这边需要特别去区分两个时间,一个是 total time 另一个是 self time (滑过会显示),total time 是 self time 加上底下所有在被呼叫的函式所需的时间。
举例来说,假如有个函式foo
呼叫 bar
,这时 foo
的 total time 是 foo
的 self time 加上 bar
的 self time。
在有了这个理解后,在看火焰图时,就要专注在看 self time,太长的 self time 就会需要特别注意。之所以不看 total time,是因为即使一个函式的 total time 很长,也不代表是这个函式有问题。以上方截图来讲,该函式的整个 total time 是 722.47 毫秒,虽然在图上面这个函式的呼叫看似很长,但其实不用担心,因为该函式的 self time 只有 2.46 毫秒。
查看主执行绪有没有被任务占满
最后想谈一个在看 Performance 面板时,高频率会看的,是主执行绪是否过度繁忙。在 JavaScript 中,除非使用 worker 来帮忙,不然基本上是单一执行绪。换句话说,如果主执行绪忙着处理任务,没办法妥善处理好下一轮的绘制,就会有所谓「掉帧」的状况,而对使用者来说,就会觉得浏览器很卡很顿。
下图是来自 Chrome 官方的《Analyze runtime performance》一文的范例,如果在 Performance 面板中有看到这种红色的小块状在 Main 上面,就代表主执行绪被榨干。

如《Measure performance with the RAIL mode》一文谈到的,要让人觉得画面是顺畅的,每秒需要渲染 60 帧 (也就是大家常看到的 60fps),换算起来每一帧最多只能有 16 毫秒 (1000 毫秒 / 每秒 60 帧),但因为浏览器会花约 6 毫秒处理绘制,所以真的能剩的是每帧 10 毫秒。
如果主行绪被榨干,处理速度无法达到这个频率,就会导致某些帧没被绘制,就是上面提到的掉帧的卡顿状况。要处理掉帧,透过 requestAnimationFrame
这类 Web API,能够帮上大忙。
阅读更多
在谈完以上的基本内容后,接着我们会来谈如何透过 Performance 面板查看 Web Vitals、requestAnimationFrame
实务上可以怎么被用来解决效能问题,以及如何在 Network 面板上定位问题。这些内容都在 E+ 的主题文有近一步讨论。
本文为 E+ 成长计划的深度内容,截取段落开放免费阅读。欢迎加入 E+ 成长计划阅读完整版本 (点此了解 E+ 的详细介绍)。