简介
如今,作者可以使用许多抽象来构建他们的 Web 应用。许多作者没有直接与 Web 平台提供的低级 API 对接,而是利用框架、构建工具和编译器从更高级别的角度编写应用。
例如,在 Angular 框架之上构建的组件是使用 HTML 模板使用 TypeScript 编写的。在后台,Angular CLI 和 webpack 会将所有内容编译到 JavaScript 和所谓的“软件包”中,后者随后会被交付给浏览器。
在开发者工具中调试或分析 Web 应用时,您目前会看到和调试这个已编译版本的代码,而不是您实际编写的代码。不过,作为作者,您并不希望这样做:
- 您不想调试缩减的 JavaScript 代码,而是想调试原始的 JavaScript 代码。
- 在使用 TypeScript 时,您不想调试 JavaScript,而是想调试原始 TypeScript 代码。
- 当您像使用 Angular、Lit 或 JSX 一样使用模板时,并非总是想要调试生成的 DOM。您可能需要调试组件本身。
总体而言,您可能需要在编写代码时对自己的代码进行调试。
虽然源代码映射在一定程度上已经弥补了这一差距,但 Chrome 开发者工具和生态系统在这方面还有更多作用。
一起来看看吧!
编写代码与部署代码
目前,在 Sources 面板中浏览文件树时,您会看到已编译(且通常是缩减的)bundle 的内容。这些是浏览器下载和运行的实际文件。DevTools 将其称为 Deployed Code。
这不太方便,而且通常很难理解。作为作者,您希望查看和调试自己编写的代码,而不是已部署的代码。
为弥补此损失,您现在可以让树状代码显示编写的代码。这会使布局树更接近您在 IDE 中看到的源文件,并且这些文件现在已与已部署代码分开。
如需在 Chrome 开发者工具中启用此选项,请前往设置 > 实验,然后选中将来源分组为“已编写”和“部署的树”。
“只需我的促销代码”
使用依赖项或在框架的基础上进行构建时,第三方文件可能会妨碍您的操作。大多数情况下,您只希望查看您的代码,而不是隐藏在 node_modules
文件夹中的某些第三方库的代码。
为了弥补这一问题,开发者工具默认启用了一项额外设置:自动将已知的第三方脚本添加到忽略列表。您可以在 DevTools > Settings > Ignore List 中找到它。
启用此设置后,开发者工具会隐藏框架或构建工具标记为“要忽略”的任何文件或文件夹。
从 Angular v14.1.0 开始,其 node_modules
和 webpack
文件夹的内容也带有相应标记。因此,这些文件夹、其中的文件以及其他此类第三方工件都不会出现在开发者工具中的各个位置。
作为作者,您无需执行任何操作即可启用这种新行为。是否实施此更改由框架负责。
堆栈轨迹中已忽略列出的代码
这些被忽略列出的文件不再显示的一个位置是堆栈轨迹。作为作者,您现在可以看到更多相关的堆栈轨迹。
如果您想查看堆栈轨迹的所有调用帧,可以随时点击显示更多帧链接。
这同样适用于您在调试和单步调试代码时看到的调用堆栈。当框架或捆绑器通知开发者工具有关第三方脚本的信息时,开发者工具会自动隐藏所有不相关的调用帧,并在单步调试过程中跳过所有被忽略列出的代码。
文件树中已忽略列表中的代码
如需在 Sources 面板的 Authored Code 文件树中隐藏已列入忽略名单的文件和文件夹,请在开发者工具的 Settings > Experiments 中勾选 Hide ignore-listed code in source tree view(在源代码树状视图中隐藏已列入忽略列表的代码)。
在示例 Angular 项目中,node_modules
和 webpack
文件夹现在处于隐藏状态。
“快速打开”菜单中已忽略列出的代码
被忽略的代码不仅在文件树中隐藏,还隐藏在“快速打开”菜单中(Ctrl+P (Linux/Windows) 或 Command+P (Mac))。
更多对堆栈轨迹的改进
在介绍过相关堆栈轨迹之后,Chrome 开发者工具对堆栈轨迹进行了更多改进。
关联的堆栈轨迹
当某些操作被安排异步执行时,开发者工具中的堆栈轨迹目前只讲述部分故事。
例如,下面是一个假设的 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”的新功能,通过将异步代码的两个部分链接在一起,可以讲述完整的故事。
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 开发者工具现在可以显示更好的堆栈轨迹。
请注意,businessLogic()
现在包含在堆栈轨迹中!不仅如此,任务还使用了熟悉的名称 someTask
,而不是像以前一样的通用 requestAnimationFrame
。
友好的调用框架
在构建项目时,框架往往会使用各种模板语言生成代码,例如,Angular 或 JSX 模板会将类似 HTML 的代码转换为最终在浏览器中运行的普通 JavaScript。有时,为此类生成的函数指定的名称不太好记,可能是经过缩减之后的单个字母名称,也可能是一些生僻或生僻但并不直观的名称。
在示例项目中,您在堆栈轨迹中看到的 AppComponent_Template_app_button_handleClick_1_listener
就是一个例子。
为了解决这个问题,Chrome 开发者工具现在支持通过源映射重命名这些函数。如果源映射有函数作用域开头的名称条目,调用帧应在堆栈轨迹中显示该名称。
作为作者,您无需执行任何操作即可启用这种新行为。是否实施此更改由框架负责。
展望未来
得益于本博文中概述的新增功能,Chrome 开发者工具可以为您提供更好的调试体验。该团队还想探索更多领域。特别是如何改善开发者工具中的性能分析体验。
Chrome 开发者工具团队建议框架作者采用这些新功能。案例研究:使用开发者工具更好地进行 Angular 调试提供了有关如何实现此功能的指南。