为什么要学习rollup.js
rollup.js是Javascript的ES模块打包器,我们熟知的Vue、React等诸多知名框架或类库都通过rollup.js进行打包。与Webpack偏向于应用打包的定位不同,rollup.js更专注于Javascript类库打包(虽然rollup.js也可以提供资源打包,但显然这不是它的强项)。在我们学习Vue和React等框架源码或者自己编写Javascript类库时,rollup.js是一条必经之路。
rollup.js的工作原理
rollup.js可以将我们自己编写的Javascript代码(通过插件可以支持更多语言,如Tyepscript)与第三方模块打包在一起,形成一个文件,该文件可以是一个库(Library)或者一个应用(App),在打包过程中可以应用各类插件实现特定功能。下图揭示了rollup.js的运行机制:
rollup.js默认采用ES模块标准,我们可以通过rollup-plugin-commonjs插件使之支持CommonJS标准。
安装rollup.js
rollup.js的安装依赖于nodejs,之前的手记中我曾详细介绍如何通过nvm管理nodejs版本,需要了解的小伙伴可以点击这里
全局安装rollup.js
首先全局安装rollup:
npm i rollup -g
rollup.js打包实例
安装成功后,我们尝试使用rollup做一个简单的案例,创建src目录:
mkdir src
在src目录下创建a.js:
vim src/a.js
写入如下代码,这个模块非常简单,仅仅对外暴露一个变量a:
const a = 1
export default a
在src目录下再创建main.js:
vim src/main.js
写入如下代码,这个模块会引入模块a,并对外暴露一个function:
import a from './a.js'
export default function() {
console.log(a)
}
通过rollup指令,我们可以快速地预览打包后的源码,这点和babel非常类似:
$ rollup src/main.js -f es
src/main.js stdout...
const a = 1;
function main() {
console.log(a);
}
export default main;
created stdout in 26ms
需要注意的是rollup必须带有-f参数,否则会报错:
$ rollup src/main.js
src/main.js stdout...
[!] Error: You must specify output.format, which can be one of 'amd', 'cjs', 'system', 'esm', 'iife' or 'umd'
https://rollupjs.org/guide/en#output-format-f-format
rollup的报错提示非常棒,非常有利于我们定位错误和修复问题。通过上面的错误提示,我们了解到-f的值可以为’amd’、‘cjs’、‘system’、‘esm’('es’也可以)、'iife’或’umd’中的任何一个。-f参数是--format的缩写,它表示生成代码的格式,amd表示采用AMD标准,cjs为CommonJS标准,esm(或es)为ES模块标准。接着我们把这段代码输出到一个文件中:
$ rollup src/main.js -f es -o dist/bundle.js
src/main.js dist/bundle.js...
created dist/bundle.js in 29ms
参数-o指定了输出的路径,这里我们将打包后的文件输出到dist目录下的bundle.js,这个文件内容与我们之前预览的内容是完全一致的。我们再输出一份CommonJS格式的代码:
$ rollup src/main.js --format cjs --output.file dist/bundle-cjs.js
src/main.js dist/bundle-cjs.js...
created dist/bundle-cjs.js in 27ms
参数--output.file是-o的全称,它们是等价的,输出后我们在dist目录下会多一个bundle-cjs.js文件,查看这个文件的内容:
'use strict';
const a = 1;
function main() {
console.log(a);
}
module.exports = main;
可以看到代码采用CommonJS标准编写,并且将a.js和main.js两个文件进行了融合。
验证rollup.js打包结果
在打包成功后,我们尝试运行dist/bundle-cjs.js代码:
$ node
> const m = require('./dist/bundle-cjs.js')
> m()
1
我们接着尝试运行之前输出的ES标准代码dist/bundle.js,由于nodejs并不支持ES标准,直接运行会报错:
$ node
> require('./dist/bundle.js')()
/Users/sam/Desktop/rollup-test/dist/bundle.js:7
export default main;
^^^^^^
SyntaxError: Unexpected token export
babel为我们提供了一个工具:babel-node,它可以在运行时将ES标准的代码转换为CommonJS格式,从而使得运行ES标准的代码成为可能,首先全局安装babel-node及相关工具,@babel/node包含babel-node,@babel/cli包含babel,而这两个工具都依赖@babel/core,所以建议都安装:
npm i @babel/core @babel/node @babel/cli -g
这里要注意的是babel 7改变了npm包的名称,之前的babel-core和babel-cli已经被弃用,所以安装老版本babel的同学建议先卸载:
npm uninstall babel-cli babel-core -g
然后到代码的根目录下,初始化项目:
npm init
一路回车后,在代码根目录下创建babel的配置文件.babelrc,写入如下配置
{
"presets": ["@babel/preset-env"]
}
完成babel配置后安装babel的依赖:
npm i -D @babel/core @babel/preset-env
尝试通过babel编译代码:
$ babel dist/bundle.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var a = 1;
function main() {
console.log(a);
}
var _default = main;
exports.default = _default;
可以看到ES模块代码被编译成了CommonJS格式,下面通过babel-node运行代码:
$ babel-node
> require('./dist/bundle.js')
{ default: [Function: main] }
> require('./dist/bundle.js').default()
1
注意babel会认为export default function()是一个名称为default的函数,如果想更改这个函数名称,可以修改main.js:
import a from './a.js'
export function test() {
console.log(a)
}
重写打包后通过babel-node运行:
$ rollup -f es --file dist/bundle.js src/main.js
src/main.js dist/bundle.js...
created dist/bundle.js in 26ms
$ babel-node
> require('./dist/bundle.js').test()
1
注意这里的--file定价于-o和--output.file,通过上述案例,我们完成了rollup打包的基本操作,并验证了打包结果。但很多时候我们不会这样操作,因为直接使用命令行功能单一,而且无法使用插件,所以我们需要借助配置文件来操作。