我们是开发者工具团队中 TypeScript 的忠实粉丝,因此,他们正使用开发者工具中的新代码编写新代码,并且我们正在对整个代码库进行大规模迁移,以便通过 TypeScript 进行类型检查。如需详细了解此次迁移,请参阅 2020 年 Chrome 开发者峰会上的演讲。因此,我们也考虑将 Puppeteer 的代码库迁移到 TypeScript。
计划迁移
在规划如何迁移时,我们希望能够循序渐进地取得进展。这降低了迁移的开销(无论何时,您都只需要编写一小部分代码),并且降低了风险。如果其中某个步骤出现问题,你可以轻松将其还原。Puppeteer 有许多用户,如果发布版本有问题,会给许多用户带来麻烦,因此尽量降低破坏性更改的风险至关重要。
我们还很幸运地发现 Puppeteer 提供了一组强大的单元测试,涵盖了其所有功能。这意味着,我们可以确信自己在迁移过程中不会破坏代码,也不会对 API 进行更改。迁移的目标是让所有 Puppeteer 用户都不会察觉到迁移,而测试是该策略的重要组成部分。如果没有良好的测试覆盖率,我们会在继续迁移之前添加测试覆盖率。
在没有测试的情况下执行任何代码更改都很危险,但更改整个文件或整个代码库的更改尤其危险。在进行机械更改时,很容易遗漏某个步骤,而且在很多情况下,测试发现了实施者和审核者都忽略的问题。
我们在前期花费了一些时间来设置持续集成 (CI)。我们注意到,针对拉取请求的 CI 运行不稳定,并且经常失败。这种情况发生得如此频繁,以至于我们养成了忽略 CI 并合并拉取请求的习惯,假定失败是 CI 上的一次性问题,而不是 Puppeteer 中的问题。
经过一些常规维护和专门的时间来修复一些常规测试不稳定问题,我们使其进入更持续的通过状态,使我们能够监听 CI,并且知道故障表示实际问题。这项工作并不光鲜,而且看着无休止的 CI 运行会让人感到沮丧,但鉴于迁移会向我们的测试套件发送大量拉取请求,确保测试套件可靠地运行至关重要。
选择并提交一个文件
此时,我们的迁移工作已就绪,我们准备了一个充满测试的强大 CI 服务器来留心监督。我们有意选择了一个小文件进行迁移,而不是深入研究任意文件。这项练习非常有用,因为它可以让您验证即将执行的计划流程。如果此方法适用于此文件,则说明您的方法有效;如果不适用,您可以重新开始。
此外,逐个文件进行更改(并使用常规 Puppeteer 版本,以免所有更改都发布在同一 npm 版本中)可降低风险。我们选择了 DeviceDescriptors.js
作为第一个文件,因为它是代码库中最简单的文件之一。完成所有这些准备工作并进行如此小的更改,可能会让人略感失望,但我们的目标不是立即进行重大更改,而是要小心谨慎地逐个文件进行更改。在验证此方法时花费的时间,在迁移过程中遇到更复杂的文件时,肯定会节省时间。
证明模式并重复
幸运的是,对 DeviceDescriptors.js
的更改成功纳入了代码库,并且方案也按预期发挥了作用!至此,您已经准备好全力以赴,这也是我们所做的。使用 GitHub 标签是将所有拉取请求归为一组的绝佳方式,我们发现这对跟踪进度很有帮助。
迁移并稍后改进
对于任何单个 JavaScript 文件,我们的流程如下:
- 将文件从
.js
重命名为.ts
。 - 运行 TypeScript 编译器。
- 解决所有问题。
- 创建拉取请求。
这些初始拉取请求中的大部分工作都是为现有数据结构提取 TypeScript 接口。对于我们之前讨论过的迁移 DeviceDescriptors.js
的第一个拉取请求,代码从以下代码变为:
module.exports = [
{
name: 'Pixel 4',
… // Other fields omitted to save space
},
…
]
变为:
interface Device {
name: string,
…
}
const devices: Device[] = [{name: 'Pixel 4', …}, …]
module.exports = devices;
在此过程中,我们检查了代码库中的每一行代码,以查找问题。与已经存在几年时间并随着时间推移而增加的任何代码库一样,重构代码并改善情况也存在一些机会。尤其是在改用 TypeScript 后,我们发现在某些地方,稍微重构一下代码就能让我们更依赖于编译器,并获得更好的类型安全性。
与直觉相反,不要立即做出这些更改,这一点非常重要。迁移的目的是将代码库转换为 TypeScript,在大规模迁移过程中,您应始终考虑对软件和用户造成破坏的风险。通过尽量减少最初的更改,我们将这种风险保持在较低水平。合并并迁移到 TypeScript 文件后,我们可以进行后续更改,以改进代码以便利用类型系统。请务必为迁移设置严格的边界,并尽量不要超出这些边界。
迁移测试以测试我们的类型定义
将整个源代码迁移到 TypeScript 后,我们就可以将注意力转向测试了。我们的测试覆盖率很高,但全部采用 JavaScript 编写。这意味着他们没有测试的一件事是我们的类型定义。该项目的一个长期目标(我们仍在努力实现)是通过 Puppeteer 开箱即用地提供高质量的类型定义,但我们的代码库中没有任何关于类型定义的检查。
通过将测试迁移到 TypeScript(按照相同的过程,逐个文件进行迁移),我们发现了 TypeScript 中的问题,否则就只能由用户来发现。现在,我们的测试不仅涵盖我们的所有功能,而且还可对 TypeScript 进行质量检查!
作为 Puppeteer 代码库的工程师,TypeScript 给我们带来了极大的助益。结合我们大大改进的 CI 环境,这让我们在使用 Puppeteer 时提高了工作效率,并让 TypeScript 捕获了原本会进入 npm 版本的 bug。我们很高兴能够发布高质量的 TypeScript 定义,让使用 Puppeteer 的所有开发者也能受益于这项工作。
下载预览渠道
请考虑将 Chrome Canary、开发者版或 Beta 版用作您的默认开发浏览器。通过这些预览版渠道,您可以使用最新的 DevTools 功能、测试尖端的 Web 平台 API,并帮助您在用户发现问题之前发现网站上的问题!
与 Chrome DevTools 团队联系
您可以使用以下选项讨论与 DevTools 相关的新功能、更新或任何其他内容。
- 请访问 crbug.com 向我们提交反馈和功能请求。
- 在 DevTools 中,依次选择 More options > Help > Report a DevTools issue 以报告 DevTools 问题。
- 向 @ChromeDevTools 发送推文。
- 在 “开发者工具的新变化”YouTube 视频或 “开发者工具提示”YouTube 视频中留言。