简介
如今,作者可以使用许多抽象化来构建他们的 Web 应用。许多作者会利用框架、构建工具和编译器,从更高层次的角度编写应用,而不是直接与 Web 平台提供的低级 API 交互。
例如,基于 Angular 框架构建的组件是通过 HTML 模板以 TypeScript 编写的。在后台,Angular CLI 和 webpack 会将所有内容编译为 JavaScript 并编译到所谓的软件包中,然后将其发送到浏览器。
在 DevTools 中调试或分析 Web 应用时,您目前可以查看和调试此已编译的代码版本,而不是您实际编写的代码。不过,作为作者,您肯定不希望出现以下情况:
- 您不想调试经过缩减的 JavaScript 代码,而是想调试原始 JavaScript 代码。
- 使用 TypeScript 时,您不想调试 JavaScript,而是要调试原始 TypeScript 代码。
- 使用模板(例如 Angular、Lit 或 JSX)时,您并不总是需要调试生成的 DOM。您可能需要调试组件本身。
总的来说,您可能希望按照编写代码的方式调试自己的代码。
虽然源代码映射在一定程度上缩小了这一差距,但 Chrome 开发者工具和生态系统还可以在这方面做更多工作。
一起来看看吧!
已授权代码与已部署代码
目前,在 Sources 面板中浏览文件树时,您可以看到已编译(通常经过缩减)的软件包的内容。这些是浏览器下载和运行的实际文件。DevTools 将其称为已部署的代码。
这不太方便,而且通常很难掌握。作为作者,您希望查看和调试自己编写的代码,而不是已部署的代码。
为此,您现在可以让该树改为显示已创建的代码。这会使树更接近您在 IDE 中看到的源文件,并且这些文件现在已与已部署代码分离开来。
如需在 Chrome 开发者工具中启用此选项,请依次前往 Settings > Experiments,然后选中 Group sources into Authored and Deployed trees。
“仅我的代码”
使用依赖项或在框架上构建时,第三方文件可能会给您带来麻烦。在大多数情况下,您只想查看自己的代码,而不是 node_modules
文件夹中隐藏的某些第三方库的代码。
为了弥补这一点,开发者工具默认启用了一项额外设置:自动将已知的第三方脚本添加到忽略列表。您可以在 DevTools > Settings > Ignore List 中找到它。
启用此设置后,DevTools 会隐藏框架或构建工具标记为要忽略的任何文件或文件夹。
从 Angular v14.1.0 开始,其 node_modules
和 webpack
文件夹的内容已进行相应标记。因此,这些文件夹、其中的文件以及其他此类第三方工件不会显示在 DevTools 的各个位置。
作为作者,您无需执行任何操作即可启用此新行为。具体实现方式取决于框架。
堆栈轨迹中被忽略的代码
这些已列入忽略列表的文件不再显示的一个位置是堆栈轨迹。作为作者,您现在可以查看更多相关的堆栈轨迹。
如果您想查看堆栈轨迹的所有调用帧,可以随时点击显示更多帧链接。
这同样适用于您在调试和单步调试代码时看到的调用堆栈。当框架或捆绑工具向开发者工具告知第三方脚本时,开发者工具会自动隐藏所有不相关的调用帧,并在逐步调试时跳过列入忽略列表的所有代码。
文件树中被忽略的代码
如需从 Sources 面板中的 Authored Code 文件树中隐藏列入忽略列表的文件和文件夹,请在 DevTools 的 Settings(设置)> Experiments(实验)中勾选 Hide ignore-listed code in sources tree view(在源代码树视图中隐藏列入忽略列表的代码)。
在示例 Angular 项目中,node_modules
和 webpack
文件夹现已隐藏。
“快速打开”菜单中忽略列出的代码
列入忽略列表的代码不仅会从文件树中隐藏,还会从“快速打开”菜单(Ctrl+P [Linux/Windows] 或 Command+P [Mac])中隐藏。
进一步改进了堆栈轨迹
除了相关堆栈轨迹之外,Chrome DevTools 还对堆栈轨迹进行了更多改进。
关联的堆栈轨迹
当某些操作被安排以异步方式执行时,开发者工具中的堆栈轨迹目前只能提供部分信息。
例如,以下是一个假设的 framework.js
文件中的非常简单的调度程序:
function makeScheduler() {
const tasks = [];
return {
schedule(f) {
tasks.push({ f });
},
work() {
while (tasks.length) {
const { f } = tasks.shift();
f();
}
},
};
}
const scheduler = makeScheduler();
function loop() {
scheduler.work();
requestAnimationFrame(loop);
};
loop();
...以及开发者如何在自己的 example.js
文件中的代码中使用它:
function someTask() {
console.trace("done!");
}
function businessLogic() {
scheduler.schedule(someTask);
}
businessLogic();
在 someTask
方法内添加断点或检查控制台中输出的轨迹时,您不会看到任何提及 businessLogic()
调用(该调用是此操作的“根本原因”)的内容。
您只会看到导致任务执行的框架调度逻辑,而不会在堆栈轨迹中看到任何面包屑,以帮助您找出导致此任务的事件之间的因果关系。
得益于一项名为“异步堆栈标记”的新功能,我们最终可以通过将异步代码的两个部分关联起来,了解整个过程。
Async Stack Tagging API 引入了名为 console.createTask()
的新 console
方法。API 签名如下所示:
interface Console {
createTask(name: string): Task;
}
interface Task {
run<T>(f: () => T): T;
}
console.createTask()
调用会返回一个 Task
实例,您稍后可以使用该实例来运行任务的内容 f
。
// Task Creation
const task = console.createTask(name);
// Task Execution
task.run(f);
该任务会在其创建的上下文与正在执行的异步函数的上下文之间建立关联。
应用于上面的 makeScheduler
函数后,代码会变成以下内容:
function makeScheduler() {
const tasks = [];
return {
schedule(f) {
const task = console.createTask(f.name);
tasks.push({ task, f });
},
work() {
while (tasks.length) {
const { task, f } = tasks.shift();
task.run(f); // instead of f();
}
},
};
}
因此,Chrome DevTools 现在能够显示更准确的堆栈轨迹。
请注意 businessLogic()
现在是如何包含在堆栈轨迹中的!不仅如此,任务还具有熟悉的名称 someTask
,而不是像以前一样的通用 requestAnimationFrame
。
友好的调用帧
在构建项目时,框架通常会使用各种模板语言生成代码,例如 Angular 或 JSX 模板,它们会将看起来像 HTML 的代码转换为最终在浏览器中运行的纯 JavaScript 代码。有时,系统会为这类生成的函数提供不太友好的名称,例如经过缩减后的单个字母名称,或者一些不明确或不熟悉的名称(即使未经过缩减)。
在示例项目中,堆栈轨迹中显示的 AppComponent_Template_app_button_handleClick_1_listener
就是一个示例。
为了解决这个问题,Chrome 开发者工具现在支持通过源映射重命名这些函数。如果源映射包含函数作用域开头的名称条目,调用帧应在堆栈轨迹中显示该名称。
作为作者,您无需执行任何操作即可启用此新行为。具体实现方式取决于框架。
展望未来
得益于本文中介绍的新增功能,Chrome 开发者工具可为您提供更出色的调试体验。该团队还希望探索更多领域。具体而言,如何改进 DevTools 中的性能分析体验。
Chrome DevTools 团队建议框架作者采用这些新功能。案例研究:使用开发者工具更好地调试 Angular提供了有关如何实现此目的的指导。