前言
之前一直用 webpack
来打包前端项目,虽说配置复杂,但用多了也沉淀出了自己的配置,能够应对多数的打包和构建场景。然而最近在折腾一些自己的小项目时,需要打包一些类库发布到 npm
。虽说 webpack
也可以构建 library
,但是打包出的产物体积过大,而且代码也不是那么的“干净”,并且当想通过 webpack
一次打包出不同的版本,比如 esm
、 CommonJS
、 umd
等,webpack
就显得更加难用,于是就把目光锁定到了 rollup
。
什么是 rollup
rollup
和 webpack
一样,都是 JavaScript 模块打包器
,用于打包和构建 JavaScript
应用程序和 library
。而 rollup
则更适合打包 library
且自身更为小巧和简单,所以有些开发应用时需要的功能,rollup
反而不支持,比如模块热更新(HMR)
。我们熟知的 Vue
、 React
都是通过 rollup
打包。并且 rollup
进入大多是开发者的视野也要得益于 React
。2017年4月初,Facebook 将一个巨大的 pull 请求 合并到了 React 主分支中,将其现有的构建流程替换为基于 rollup
,这一举动让 rollup
得到了更多开发者的关注。
快速上手
首先创建项目,我们将会实现一个计算器类库,用于解决 js 在计算加减乘除时产生的精度丢失问题。项目目录如下:
仓库地址: https://github.com/onlymisaky/calculator
calculator |
index.js
文件中包含 加减乘除
四个方法,并通过匿名导出向外部暴露这些方法:
export default { |
utils.js
中则是一些辅助方法,无需多言。
由于只是一个简单的计算功能,并不设计和平台有关的功能或 API
,所以我们要实现的这个 library
可以在任意的 js 环境下使用,比如浏览器
、 node
、 Electron
等等。所以我们也需要构建出不同版本的包。
接下来就开始正式进入 rollup
的正式使用。首先就是安装 rollup
:
npm i rollup -D |
和 webpack
一样, rollup
可以通过命令的方式直接使用(需要将 rollup
全局安装):
rollup src/index.js -f umd -o dist/index.js |
上面的命令表示将 src/index.js
以 umd 形式打包,输出到 dist/index.js
。
很显然这种方式及其不灵活,所以只提一下不做详细的参数介绍。
最常用的方法还是使用配置文件的方式,不必担心, rollup
的配置文件比 webpack
简单多了,甚至比 gulp
的还简单。
核心概念
在介绍如何编写 rollup
的配置文件前,我么需要先了解几个它的核心概念,这有助于我们更好使用。
input
: 入口文件,对标webpack
的entry
,指明了库文件入口位置output
: 输出文件,对标webpack
的output
,指明了打包后输出文件的位置、包名、格式等等plugins
: 插件,在构建过程中,需要一些辅助功能,都通过插件实现,比如语法转换、别名解析external
: 当我们的库是基于另一个库开发时,就需要用到它,比如开发一个基于Vue
的指令,为了不将 Vue 打包到我们的库中,就需要将 Vue 写在 external 中。
以上就是 rollup
的一些核心概念,相较于 webpack
确实简化了许多,在了解核心概念后,编写 rollup
配置文件就简单多了。
编写和使用配置文件
在项目根目录创建 rollup.config.js
文件,代码内容如下:
import pkg from './package.json'; |
上面的配置代码中,只涉及到了 input
和 output
两个概念,需要额外解释的是 output
这个选项。
output
允许传入一个对象会数组,当传入数组时,会依次输出多个文件。
output.file
: 表示输出的文件路径output.format
: 表示输出文件的格式,可选项有umd
、commonjs
、esm
等。output.name
: 当format
值为umd
时,需要设置output.name
,在浏览器环境下就可通过name
使用。output.name
: 导出方式,可选值有default
、named
、none
、auto
,我们是匿名导出,就填写default
。output.banner
: 文件头部添加的内容,当然也有对应的output.footer
选项用于文件末尾添加的内容 。
接下来使用 rollup -c
命令便可以打包,就可以在 dist
文件夹输出 index.js
文件。为了方便,将 rollup -c
加入 npm scripts
中,运行 npm run build
,得到的输出文件内容大致如下:
|
这是一个标准的 umd
格式的包,我们可以在 浏览器环境下通过 Calculator
直接使用,也可以在 node
环境下通过 requrie
调用。
babel 插件
大致浏览打包出的代码后,可以发现有些较新的语法被没有转换,比如 模板字符串
、 箭头函数
、 rest参数
等等,这些代码在 es5
环境下是无法运行的。为什么会这样呢?因为 rollup 是不会转换这些的,那么就需要用到 babel
插件,来转换这些新的语法。
npm i @babel/core @babel/preset-env @rollup/plugin-babel -D |
rollup.config.js
文件中添加 plugin
配置项:
import babel from '@rollup/plugin-babel'; |
创建 babel.config.js
配置文件:
/** @type {import('@babel/core').TransformOptions} */ |
配置好 babel
之后,再次打包,我们以发现一些高级的语法和 API (这里笔者偷懒了,实际上在没有安装和配置 @babel/runtime
等相关插件前,babel 只能转换语法,不能转化 API ,比如代码中的 includes 就是新的 API 但是并没有被转换,等笔者写完 babel 相关文章后再来更新) 已经被转换可以在低版本环境中运行了。
resolve 插件
在上述场景中,我们并没有引用其他的包,但在实际开发中引用第三方包辅助快速开发是非常常见的场景。
在默认情况下,如果我们直接导入 node_modules
中的包,打包完成之后,node_modules
中的包并不会和我们编写的库合并。为了举例说明,我们将安装一个用于迭代字符串的包 repeat-string
,然后在 index.js
导入并使用:
import repeat from 'repeat-string'; |
在运行 npm run build
之后可以发现,虽然打包成功了,但是控制台确有一些警告提示:
(!) Unresolved dependencies |
打包后的代码中也可以看到,repeat-string
默认是以参数的形式注入其中,而不是与我们的代码合并。
为了解决这个问题,就要用到 @rollup/plugin-node-resolve
插件,他可以帮助我们解析 node_modules
中的第三方包。
npm i @rollup/plugin-node-resolve -D |
rollup.config.js
:
import babel from '@rollup/plugin-babel'; |
再次运行 npm run build
之后,你会惊讶的发现,直接报错了:
src/index.js → dist/index.js... |
这是因为 repeat-string
是 commonjs
格式的包,而我们是以 ESModule
的方式引入的,所以就报错了。所以需要继续借助插件将 commonjs
转换为 ESM
,就是接下来要介绍的 commonjs 插件。
commonjs 插件
安装和配置:
npm i @rollup/plugin-commonjs -D |
import babel from '@rollup/plugin-babel'; |
npm run build
之后,可以发现不仅正确的解析了 repeat-string
,而且也将代码和我们的库合并了。
其他常用插件
除了上述的三个插件之外,还有一些常见的插件:
@rollup/plugin-json
: 解析编译源码中的json
文件,并且配合rollup
的Tree Shaking
可只打包.json
文件中我们真正用到的部分。@rollup/plugin-typescript
: 解析和转换typescript
@rollup/plugin-eslint
: eslint 插件rollup-plugin-terser
: 压缩代码
其它更多插件可以到官方仓库中查找: https://github.com/rollup/plugins
external 属性
当配置了 @rollup/plugin-node-resolve
和 @rollup/plugin-commonjs
之后, 所有从 node_modules
中导入的包都会合并到我们的库中,有时候我们并不希望如此,比如 Vuex
和 VueRouter
都基于 Vue
开发,但打包出的代码中若包含了 Vue
的源码,那显然不合适,所以需要将 Vue
设置为外部项,也就是通过 external
属性来设置。如果回到我们的案例中,我们想将 repeat-string
也设置为外部项,只需做如下修改:
import resolve from '@rollup/plugin-node-resolve'; |