如今,开发者通常会利用框架、构建工具和编译器从更高级别的角度来编写 Web 应用程序。在 DevTools 中调试或分析 Web 应用程序时,目前能查看和调试的都是已经编译的代码,而不是实际编写的代码。很多时候,我们并不想调试已经编译、压缩后的代码,而是想调试原始代码:
使用 TypeScript 时,不想调试编译之后的 JavaScript,而是想调试原始的 TypeScript 代码;
当使用 Angular、JSX 之类的模板时,并不希望调试生成的 DOM。而是调试自己编写的组件。
总而言之,我们可能希望在编写自己的代码时对其进行调试。虽然 source maps 已经在一定程度上缩小了这一差距,但 Chrome DevTools 和生态系统可以在这方面做更多的事情。下面就来看一看吧!
1. Deployed 和 Authored 代码
目前,在 Sources 面板中导航文件树时,可以看到已编译(通常已压缩)包的内容,这些是浏览器下载和运行的实际文件。DevTools 将其称为 Deployed 代码。
这在调试时可能并不是很方便,我们希望查看和调试自己编写的代码,而不是打包完的代码。为了弥补这一点,现在可以通过树来显示我们编写代码(这被称为 Authored 代码)。这使得树更接近于在 IDE 中看到的源文件,并且这些文件现在与 Deployed 代码是分离的:
可以在 Chrome DevTools 中开启此选项:DevTools > Settings > Experiments > Group sources into Authored and Deployed trees.
注:Chrome DevTools 团队计划很快默认启用此实验选项。
2. Just my code
注:这是 Chrome Canary 106 版本的预览功能。
当使用依赖项或在框架之上构建应用时,第三方文件可能会阻碍我们的调试。多数时候,我们只想调试自己编写的代码,而不是隐藏在 node_modules 文件中的某些第三方库的代码。
为了弥补这一点,DevTools 默认启用了一个额外的设置:自动将已知的第三方脚本添加到忽略列表。可以在 DevTools > Settings > Ignore List 中找到它。
启用此设置后,DevTools 会隐藏框架或构建工具标记为忽略的任何文件或文件夹。作为开发者,我们不需要做任何事情来启用这种新的行为,具体实施取决于框架。比如,从 Angular v14.1.0 开始,其 node_modules 和 webpack 文件的内容已被标记为忽略。因此,这些文件夹、其中的文件等都不会出现在 DevTools 的各个位置。
(1)在堆栈跟踪中
这些被忽略的文件不再出现在堆栈跟踪中,现在可以看到更多相关的堆栈跟踪信息:
如果想查看堆栈跟踪的所有调用帧,可以单击显示更多帧链接。
这同样适用于在调试和单步执行代码时看到的调用堆栈。当框架或打包程序通知 DevTools 的第三方脚本时,DevTools 会自动隐藏所有不相关的调用框架并在单步调试时跳过任何被忽略的代码。
(2)在文件树中
要想在 Sources 面板的 Authored 代码树中隐藏忽略的文件和文件夹,需要在 DevTools 的 Settings > Experiments 中选中 Hide ignore-listed code in sources tree view。
在示例的 Angular 项目中,node_modules 和 webpack 文件夹现在已经隐藏了:
(3)在快速打开菜单中
被忽略的代码不仅可以在文件树中隐藏,还可以在“快速打开”菜单(快捷键:Control+P (Linux/Windows) 或 Command+P (Mac))中隐藏。
3. 对堆栈跟踪的改进
Chrome DevTools 对堆栈跟踪引入了更多改进。
(1)Linked 堆栈跟踪
当某些操作是异步触发时,DevTools 中的堆栈跟踪目前只能显示部分情况。例如,在 framework.js 文件中有一个简单的 scheduler:
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 文件中使用 scheduler:
function someTask() {
console.trace("done!");
}
function businessLogic() {
scheduler.schedule(someTask);
}
businessLogic();
在 someTask 方法中添加断点或检查控制台中打印的跟踪时,看不到任何与 businessLogic() 调用相关的信息:
在堆栈跟踪中也看不到面包屑来帮助我们找出导致该任务的事件之间的因果关系。多亏了一个名为“Async Stack Tagging”的新功能,通过将异步代码的两个部分链接在一起,解决了这个问题。
Async Stack Tagging API 引入了一个名为 console.createTask() 的新控制台方法。API 签名如下:
interface Console {
createTask(name: string): Task;
}
interface Task {
run<T>(f: () => T): T;
}
console.createTask() 调用返回一个 Task 实例,可以使用它来运行任务的内容 f。
// 任务创建
const task = console.createTask(name);
// 任务执行
task.run(f);
该任务形成了创建它的上下文和正在执行的异步函数的上下文之间的链接。
注:Async stack tagging API 将在 Chrome 106 中可用。
把它应用到 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。
(2)友好的调用帧
在构建项目时,框架通常会从各种模板语言生成代码,例如 Angular 或 JSX 模板,它们将看起来像 HTML 的代码转换为最终在浏览器中运行的纯 JavaScript。有时,这些生成的函数被命名为不太友好的名称——要么是压缩后的单字母名称,要么是一些晦涩或不熟悉的名称。
在示例项目中,包含一个名称:AppComponent_Template_app_button_handleClick_1_listener,可以在堆栈跟踪中看到它:
为了解决这个问题,Chrome DevTools 现在支持通过 source maps 重命名这些函数。作为开发者,我们无需执行任何操作即可启用此功能,具体实施取决于框架。
通过本文列出来的新增功能,Chrome DevTools 可以为我们提供更好的调试体验!Chrome 团队还想探索更多领域,特别是如何改善 DevTools 中的分析体验。期待 Chrome DevTools 未来可以带来更友好的调试体验!
参考:https://developer.chrome.com/en/blog/devtools-modern-web-debugging/