4

爱彼迎将JavaScript代码打包工具从Webpack改用Metro,缩短了构建时间

 1 year ago
source link: https://www.fly63.com/article/detial/11728
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

与许多大规模公司一样,随着代码库不断变大,爱彼迎也在打包工具方面经历了阵痛。即使代码库增至四倍,爱彼迎在2018年将JavaScript代码打包工具从webpack迁移到Metro后,还是加快了对前端的UX更改。

构建性能显著提升后,从交互时间(TTI)这个指标来看,UI更改速度加快了80%。即使是最慢的生产级构建,编译49000个模块(JavaScript文件)现在也快了55%,从使用Webpack时的30.5分钟缩短至13.8分钟。

至于使用Metro构建的那些页面,爱彼迎自己的页面性能得分(PPS)也有所提高(提高约1%)。

62a9414cb0fc6.jpg

作为参考,在2018年左右代码库增至四倍之后,简单一行代码更改的平均页面刷新时间在30秒到2分钟之间,具体取决于项目大小。

爱彼迎软件工程师Rae Liu在最近的这篇 博文 (中介绍了Webpack和Metro的一些差异,并讨论了一些迁移挑战。

一、Metro简介

Metro由Meta开发,是面向react Native的开源JavaScript代码打包工具。本文介绍Metro的自定义版本,因为爱彼迎的架构不包含React Native。降了与本公司的团队合作外,爱彼迎的工程师还直接与Meta的Metro工程师合作,进一步开发这项技术

Metro按以下顺序将打包分为三个步骤:解析、转换和序列化。

  • 解析:解析import/require语句。
  • 转换:转译代码(源到源编译器将现代Typescript/JavaScript源代码转换成JavaScript,并与旧浏览器向后兼容),典型的工具是babel
  • 序列化:将转换后的文件组合成包。

在开发过程中,爱彼迎工程师创建了一个带自定义端点的Metro服务器系统,以处理构建依赖图和源映射、转换以及捆绑JS和css文件。至于生产级构建,他们将Metro作为Node api来运行,以处理解析、转换和序列化。

迁移分两个阶段进行。最要紧的是Metro开发服务器,因为缓慢的Webpack开发服务器是导致开发生产力成本高昂的根源。第二个迁移阶段致力于让Metro在功能上与Webpack相当,并在生产环境下在Metro和Webpack之间运行A/B测试。

二、Metro和Webpack的两大区别

按需处理JavaScript包

Webpack在启动时预编译整个项目,而Metro只编译需要的内容。也就是说,JavaScript包严格上来说就是序列化的依赖图,其中入口点是这张图的根(root)。

在爱彼迎,每个前端项目都有一个Node服务器来匹配通向特定入口点的路由。请求网页时,dom包含带有开发JavaScript URL的脚本标签。Webpack需要知道所有页面的所有入口点,之后才能开始打包,而Metro只需要一个入口点,就可以根据请求处理JavaScript包。

开发人员对页面A进行了更改,但下图中看不到:

62a9418e223f1.jpg

在上图的1a和1b中,浏览器加载页面A(1),从打包工具请求entryPageA.js文件(2),打包工具使用适当的包响应浏览器(4)。图1a和图1b的区别在于操作(3),因为 Webpack图编译了页面B和C的入口点,而Metro并非如此,因为开发人员在示例中只修改了页面A。

爱彼迎最大的前端项目之一有26000个独特的模块,每页的模块中位数约7.2个模块。由于使用了服务器端渲染,爱彼迎最终要处理的模块数量翻番,达到大约48000个。在将Metro的按需编译模型付诸实施之后,现在减少了大约70%的工作量。

62a9418506ec7.jpg

爱彼迎利用Metro的多层缓存功能以及持久性和非持久性缓存。Metro允许工程师定义缓存实现,包括混合不同类型的缓存层,这样提供了更高的缓存灵活性。

爱彼迎按优先级对缓存层排序。如果在一个缓存层中没有找到结果,将使用下一层,直至找到结果。与没有缓存的默认Metro实现相比,在一个编译22000个文件的项目中,命中远程只读缓存可使服务器构建速度提高56%。

第三个缓存层是远程只读缓存而不是读写缓存,因为写入到远程缓存会带来昂贵的网络调用,尤其是在慢速网络上。这个决定在开发中另外节省了17%的构建时间。

Webpack有一个缓存层,不过它与Metro提供的缓存层不一样。

三、包拆分

爱彼迎的博文中详述的技术挑战之一是包拆分(Bundle Splitting)。这是通过动态导入边界拆分包的过程(又叫代码拆分)。开箱即用的Metro解决方案为每个入口点生成约5MiB 的巨大包,这对浏览器资源和网络延迟造成了负担,无法进行HTTP缓存。

62a9416d8f906.jpg

在上图中,import(‘./file’) 表示动态导入边界。左侧的包(3a)被拆分成右侧的三个小包(3b)。执行import('./file') 语句时请求额外的包。

假设fileA.js发生了更改,需要重新下载整个包,以便浏览器获取fileA.js中的更改。如图3b所示,由于包由动态导入拆分,fileA.js中的更改只导致重新下载fileA.js包。其余的包可以重用浏览器缓存内容。

在生产环境下,没有开发服务器,包是预先构建的。爱彼迎工程师从Webpack的包拆分算法中获得了灵感,实现了一种类似的机制来拆分Metro依赖图。与动态导入边界的开发拆分相比,爱彼迎上生成的包大小减少了约20%(由1549 KB变成1226 KB)。

开发包的优化方式不一样,因为运行包拆分算法需要时间,工程师们不想在开发中浪费时间来拆分包大小。在开发情形下,页面加载性能的优先级高于包大小实现最小化。

Metro和Webpack在包大小方面的指标具有可比性。

标题:  Airbnb Moves from Webpack to Metro, Enjoys Shorter Build Times   ,作者:Jessica Wachtel

链接: https://www.fly63.com/article/detial/11728


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK