起因
目前在开发一个jsonschema 渲染列表页的npm 包,采用的rollup 打包。刚开始一切安好,但最近发现打包速度越来越慢,差不多需要35s,简直无法忍受。尤其是在近期需求变多,常常被需求催促的时候,改一行代码需要等待半分钟才能看到效果,简直心态爆炸。于是决定专门抽时间优化一波。

实操
首先我们需要有一个大致的优化思路,时间优化,在计算机领域算是一个老生常谈的话题了,也有很多包治百病的套路:
- 减少打包文件的数量
- 减少不必要的执行函数
- 增加打包缓存,进行增量打包
- 由单线程变为多线程
基于这个思路,让我们来开始优化吧。首先贴一下最开始的rollup.config.js

设置external
作为一个npm lib ,确实有一些第三方依赖,应该做为peerDependencies引入,并且在打包的时候加入到external中。在我原来的配置当中,我使用了rollup-plugin-peer-deps-external
插件,会自动的把peerDependencies 生成到external 配置中,无需额外配置。
在原来的peerDependencies 中,我放入了react、antd 等几个理论上一定得放入peerDependencies 的包。
而目前,为了加速打包流程,我考虑把一些可加可不加的第三方依赖加入到external 中,例如我所依赖的一个比较大的包@byted-search/jsonschema-form
等。当我新增了三个peerDependencies 后,打包时间从39.5s降低到了29.7s,收效很明显。
原理很好理解,加入到external 中的包,rollup 不会进行处理,会直接依赖外部的包。

配置babel
Babel 在打包的过程中,执行了很多耗时操作,对babel 插件的优化,应该能很好的提升打包效率。
首选我们想的的就是减少不必要的打包文件,最基本的就是设置exclude: "**/node_modules/**"
,不去操作node_modules 里面的文件。但我们发现,在之前的配置中我已经设置了。
这时我注意到了控制台的一行提示:
babelHelpers: ‘bundled’ option was used by default. It is recommended to configure this option explicitly, read more here: https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers
点开链接可以看到具体的介绍,大致意思是说:
Bable 插件有一个
babelHelpers
的配置参数,取值为’bundled’ | ‘runtime’ | ‘inline’ | ‘external’,默认是’bundled’。当你在开发一个应用的时候,建议使用’bundled’,会把bable 的一些helpers 函数直接打包进去; 如果你是开发lib,那么更加推荐
runtime
,这种方式不会打包这些helpers ,而是在开发者最终打包的时候一起打包。
最开始其实我是想让使用者用起来更加方便,所有就使用了默认值。现在看完文档,觉得文档说的也有道理,另外少打包文件,一定是能加快我的编译速度的。于是我果断改了配置为’runtime’。
另外,除了改配置,我们还需要把@babel/runtime
加到dependencies
中,保证开发者本地会安装@babel/runtime
,然后再在rollup.config.js
中手动加上一行:
external: [/@babel\/runtime/]
重新打包下,我们可以看到,打包时间从29.7s 变成了22.2s。

使用环境变量
虽然打包时间已经缩短了1/3,但是我还是无法接受。那么一些边边角角可以优化的地方,还是要优化一下。
这里主要的一个思想是,我们的开发环境下的打包和最终给到用户的打包,一定是有一些不一样的地方。对于我们的开发环境,为了效率能提升,“简陋”一些也无妨。
只打包一种格式
我们输出给用户的,一般会包含cjs,es,umd三种格式的产物,但在开发时,其实没有必要,有一种就可以了。这里我只保留了cjs。
关闭sourceMap
Rollup 的sourceMap 我没有关闭,对本地调试还是很有帮助的。不过我在查优化资料的时候,发现@rollup/plugin-commonjs
这个插件会记录esm 到cjs 的sourceMap,默认是打开的。还不知道关闭后有什么影响,看github 有人推荐关闭,我也暂且先关闭了。
以上两步操作完之后,打包时间从22.2s 减少到了18.7s。

使用Cache
在优化的过程中我注意到,其实rollup --watch
打包,每次都是全量的重新打包。为什么不能像webpack 一样,进行增量打包,对上一次的打包结果进行缓存,只编译有更改的文件呢?
抱着希望去查了 rollup
的文档,虽然有cache 参数,但基本没什么用,而且还不知道如何用,Google也查不到。于是到github 的issue 列表里进行查找,得到的结论就是目前为止不支持cache,因为加入这个功能后可能会导致很多插件不能正常工作。
这真是一个悲伤的消息,我的大招没了。
大招includeDependencies
到目前为止,每次编译还需要18s,这我还是很难接受的。必须再想想办法。
经过之前的摸索和尝试,其实还是把第三方包添加到external 的收效最明显。我有个大胆的想法,可不可以在开发环境中把所有第三方依赖都加入到external 中?先动手试一试。
奇迹出现了!项目不仅能正常运行,打包时间直接由18s 减少到3.6 s。

这下我能接受了!
之前说过,rollup-plugin-peer-deps-external
插件,会自动的把peerDependencies 生成到external 配置中。我隐约记得,这个插件还有一个配置,可以把dependencies 也加入到external中,当时还在想为什么还有这种需求?赶紧查看下文档,果然有一个参数:includeDependencies
。
peerDepsExternal({ includeDependencies: !isProd, // speed up }),
结尾
至此,打包时间由39.5s 缩短至3.6s,减少了91% 的编译时间,暂时心满意足了。
目前刚用rollup 不久,欢迎各位大佬给一些建议和最佳实践,贴下代码。

process.env.NODE_ENV的NODE_ENV是如何配置的,使用的是rollup打包,可以在执行打包命令的地方设置变量值吗,我设置了不起效
mac 下的话,”build”: “NODE_ENV=production rollup” 就行