DevTools — 如何在开发者工具上 debug 原始程式码?

2025年8月11日

💎 加入 E+ 成長計畫 與超過 700+ 位工程師一同在社群成長,並獲得更多深度的軟體前後端學習資源

如何用浏览器开发者工具 (DevTools) 定位效能相关问题? 一文中,我们谈到可以如何透过浏览器的效能 (Performance) 分页,来定位前端应用程式的效能问题。在这一期主题文中,我们会继续探讨开发者工具 (DevTools) 中的来源(Sources) 面板,让 debug 变得更有效率、更快完成。

什么是来源面板 (sources panel)?

相信多数读者应该对浏览器的来源面板不陌生,在开启浏览器的开发者工具后,点击 Sources 分页就能够进到来源面板 (下方是浏览 E+ 的 Notion 内容站时,打开来源面板看到的内容)。

来源面板

来源面板顾名思义,是让开发者可以查看来源的地方。所谓的来源是指程式的原始码 (source code),因此我们可以在来源面板中检视各类构筑起网站的档案,包含 JavaScript 的档案、跟样式有关的 stylesheet,甚至是各类图片的资源。

而现代的开发者工具,在来源面板中有提供一些非常有用的方法,能够让开发者用更高效的方式 debug。

如何在开发者工具上 debug 原始程式码?

上一段落提到开发者可以在来源面板中,看到各类构筑起网站的资源,然而在现代前端开发中,通常浏览器端拿到的程式码,跟前端开发者最开始写的程式码,会看起来不太一样。

这是因为现代前端框架通常会把程式码做预处理,所以浏览器拿到的程式码会是处理过后的版本。所以除非是写纯 HTML、CSS、JavaScript,不然前端工程师无法直接在开发者工具的来源面板上 debug。

前端程式码的预处理有什么好处与问题?

在往下谈之前,相信如果对这块还不熟的读者,可能会问说「为什么前端框架要先做预处理,不把前端工程师写的原始版本直接给浏览器呢?」。

这是因为经过预处理的程式码,档案大小会比较小,所以浏览器在下载、解析上所需的时间也会比较短。

举例来说,浏览器拿到以下这段程式码

function a(b) {
  const c = new Date().getFullYear();
  return c - b;
}

上面的程式码功能与下面这段原始程式码是一模一样的

function calculateUserAge(birthYear: number) {
  const currentYear = new Date().getFullYear();
  return currentYear - birthYear;
}

这段原始程式码,在传给浏览器之前,经过以下的预处理

  • TypeScript 转译:因为浏览器无法直接处理 TypeScript,所以要先把 TypeScript 程式码转成 JavaScript
  • Minified 最小化:原本的程式码为了维持好的可读性,在变数命名上会用 calculateUserAge,但是对浏览器来说,用 a 的效果也是一样的,而比起原本的 calculateUserAge,只用 a 所占用的字元就比较少,也会让下载与解析比较快

除了转译与最小化,前端框架通常也会顺便处理打包 (bundling),让程式码可以不用一次载那么多。在这些步骤都完成后,通常程式码就会变得让工程师难以阅读的样子 (更长一点的程式码,最终可能转换成下面这样让人无法轻易阅读的版本)

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var

虽然说预先转译、最小化、打包能带来好处,但显然当程式码到浏览器上变这样,虽然浏览器能处理,但人类工程师要 debug 就会变得很困难。

如何 debug 被处理过,导致很难读的程式码?

上面谈到预处理过的程式码会很难 debug,那有没有方法,可以在浏览器上也可以看到原始的程式码呢?

Source map 在这时可以派上用场,根据 MDN 上的解释 (连结),所谓的 source map 是一个把被处理过的程式码,对应回原始程式码的一张表。一般来说会是透过 JSON 的形式来表示。

以 MDN 上的例子来说,假如有个 SCSS 语法如下

ul {
  list-style: none;
  li {
    display: inline;
  }
}

这个 SCSS 语法会被转换成以下的 CSS

ul {
  list-style: none;
}
ul li {
  display: inline;
}

/*# sourceMappingURL=index.css.map */

这中间的转换就是透过 index.css.map 这个 source map

{
  "version": 3,
  "sourceRoot": "",
  "sources": ["index.scss"],
  "names": [],
  "mappings": "AAAA;EACC;;AACA;EACC",
  "file": "index.css"
}

上面的这张表,关键的点是 mappings 这串有被编码过的字串,在浏览起收到后会解码,然后就会拿到相关的资讯,让浏览器知道如何对应处理过的程式码。

具体来说,mappings 中的字串是透过 base64 VLQ 来编码跟解码;先前 Svelte 与 Rollup 的原创者 Rich Harris 有一个开源专案 vlq.js (连结),就有展示如何透过 vlq.js 来处理 source map (范例连结),推荐感兴趣了解细节的读者可以看看。

因此针对「在开发者工具的来源面板要如何 debug 原始程式码」这个问题,只要透过 source map 就能做到。

阅读更多

以上我们介绍了如何在开发者工具上 debug 原始程式码?,关于使用浏览器的开发者工具,我们在 E+ 成长计划中的主题文有更深入、实用的探讨。

对更深入了解这个主题,以及其他前后端开发、软体工程、AI 工程主题感兴趣的读者,欢迎加入 E+ 成长计划一起成长 (连结)。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們