简介
如今,作者可以使用许多抽象化来构建他们的 Web 应用。许多作者利用框架、构建工具和编译器从较高层面的角度编写应用,而不是直接与网络平台提供的较低级别 API 对接。
例如,基于 Angular 框架构建的组件是使用 TypeScript 和 HTML 模板编写的。在后台,Angular CLI 和 webpack 将所有内容编译为 JavaScript,并将其编译成所谓的软件包,然后提供给浏览器。
在开发者工具中调试或分析 Web 应用时,您目前可以查看和调试代码的这个编译版本,而不是您实际编写的代码。作为作者,您并不希望出现以下结果:
- 您不想调试缩减后的 JavaScript 代码,而是想调试原始的 JavaScript 代码。
- 使用 TypeScript 时,不想调试 JavaScript,而是想调试原始的 TypeScript 代码。
- 当您使用模板(例如 Angular、Lit 或 JSX)时,有时可能并不需要调试生成的 DOM。您可能需要调试组件本身。
总的来说,您可能需要按照您编写的代码的方式来调试自己的代码。
虽然源映射已经在某种程度上缩小了这一差距,但 Chrome 开发者工具和生态系统可以在这方面做得更多。
一起来看看吧!
编写的代码与已部署的代码
目前,在 Sources 面板中的文件树中导航时,您会看到已编译(通常经过缩减)bundle 的内容。这些是浏览器实际下载并运行的文件。DevTools 将其称为已部署的代码。
这种方法不是很方便,而且通常也很难理解。作为作者,您希望查看和调试自己编写的代码,而不是已部署的代码。
为了弥补这一点,您现在可以让代码树改为显示编写的代码。这使得树更接近您在 IDE 中看到的源文件,并且这些文件现在已经与已部署代码分开。
如需在 Chrome 开发者工具中启用此选项,请依次前往设置 >实验,然后选中将来源分组为“已编写”和“已部署”树。
“只有我的代码”
使用依赖项或在框架上进行构建时,第三方文件可能会妨碍您的操作。大多数情况下,您只想查看自己的代码,而不是隐藏在 node_modules
文件夹中的某个第三方库的代码。
为了弥补这一点,开发者工具默认启用了一项额外设置:自动将已知的第三方脚本添加到忽略列表。您可以在 DevTools >设置 >忽略列表。
启用此设置后,开发者工具会隐藏框架或构建工具已标记为要忽略的任何文件或文件夹。
从 Angular v14.1.0 开始,其 node_modules
和 webpack
文件夹的内容已进行相应标记。因此,这些文件夹、其中的文件和其他此类第三方工件不会出现在开发者工具中的不同位置。
作为作者,您无需执行任何操作即可启用这一新行为。实现此更改取决于框架。
堆栈轨迹中被忽略的代码
这些被列入忽略列表的文件不会再显示在堆栈轨迹中。作为作者,您现在可以查看更多相关的堆栈轨迹。
如果您想查看堆栈轨迹的所有调用帧,可以随时点击显示更多帧链接。
这同样适用于您在调试和单步调试代码时看到的调用堆栈。当框架或捆绑器将第三方脚本的相关信息告知开发者工具时,DevTools 会自动隐藏所有不相关的调用帧,并在进行步骤调试时跳过任何被忽略的代码。
文件树中被忽略的代码
要在 Sources 面板的 Authored Code 文件树中隐藏已列入忽略列表的文件和文件夹,请在 Settings 中勾选 Hide is ignore-list code(在源代码树状视图中隐藏代码)>开发者工具中的实验。
在示例 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 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
。
友好的调用框架
在构建项目时,框架通常会使用各种模板语言生成代码,例如可将类似 HTML 的代码转换成最终在浏览器中运行的普通 JavaScript 的 Angular 或 JSX 模板。有时,这些生成的函数的名称不太好记 - 要么是经过缩减大小的单个字母名称,要么是一些生僻或不熟悉的名称(即使并非如此)。
在示例项目中,AppComponent_Template_app_button_handleClick_1_listener
就是一个例子,您可以在堆栈轨迹中看到它。
为了解决这个问题,Chrome 开发者工具现在支持通过源映射重命名这些函数。如果源映射有一个名称条目作为函数作用域的开头,则调用帧应在堆栈轨迹中显示该名称。
作为作者,您无需执行任何操作即可启用这一新行为。实现此更改取决于框架。
展望未来
得益于这篇博文中介绍的新增功能,Chrome 开发者工具可以为您提供更好的调试体验。该团队还想探索更多领域。特别是如何改善开发者工具中的性能分析体验。
Chrome DevTools 团队建议框架作者采用这些新功能。案例研究:使用开发者工具更好地进行 Angular 调试就如何实现此功能提供了指导。