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

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

​译者 | 布加迪

审校 | 孙淑娟

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

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

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

图1

作为参考,在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进行了更改,但下图中看不到:

图2

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

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

多层缓存

图2

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

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

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

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

三、包拆分

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

在上图中,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​

责任编辑:华轩 来源: 51CTO
相关推荐

2022-07-11 09:41:51

SnapFirefoxMozilla

2023-10-30 13:40:08

2021-12-25 22:29:04

WebpackRollup 前端

2018-05-30 15:33:00

打包工具Web

2022-09-26 07:11:12

数据堆栈视频模式

2021-12-17 00:02:28

Webpack资源加载

2021-06-18 06:11:26

工具WebpackSnowpack

2023-10-16 07:42:10

前端构建高性能

2023-07-21 15:26:00

数据库同步数据

2022-12-16 21:58:20

NodeJSH5zip

2024-09-02 00:00:01

2017-07-11 15:50:11

前端webpack2优化

2021-12-24 08:01:44

Webpack优化打包

2019-03-26 10:02:16

WebpackJavascript前端

2021-09-09 08:02:46

项目Bundleless架构

2023-08-22 10:13:53

模块工具JavaScrip

2024-05-27 00:00:01

2024-03-01 08:38:34

WebpackVue2sass

2021-08-13 14:00:14

Node.js开发前端
点赞
收藏

51CTO技术栈公众号