Rollup也是一款ES Module的打包器,它也可以将我们项目当中一些散落的细小模块打包打包为整块的代码,从而使得这些划分的模块可以更好地运行在浏览器环境或者是nodeJs环境。
从作用上看Rollup与Webpack非常类似,不过相比于Webpack,Rollup要小巧得多。因为Webpack在去配合一些插件的使用下,几乎可以完成我们开发过程中前端工程化的绝大部份工作。而Rollup仅仅是一个ES Module打包器,并没有一些其他额外的功能。例如Webpack中有HMR这样的对开发者非常友好的模块热替换功能,在Rollup当中就没办法完全支持。
Rollup诞生的目的并不是要与Webpack去全面竞争,它的初衷只是希望提供一个充分利用ESM各项特性的高效打包器。
安装
yarn add rollup --dev
以下为我的示例代码:
// ======================== logger.js ========================
export const log = msg => {
console.log('---------- INFO ----------')
console.log(msg)
console.log('--------------------------')
}
export const error = msg => {
console.error('---------- ERROR ----------')
console.error(msg)
console.error('---------------------------')
}
// ======================== message.js ========================
export default {
hi: 'Hey Guys, I am zce~'
}
// ======================== index.js ========================
// 导入模块成员
import { log } from './logger'
import messages from './messages'
// 使用模块成员
const msg = messages.hi
log(msg)
运行rollup打包,指定入口文件为index.js,通过format参数指定输出格式为iife,通过file参数指定输出路径。
yarn rollup ./src/index.js --format iife --file dist/bundle.js
dist/bundle.js
(function () {
'use strict';
const log = msg => {
console.log('---------- INFO ----------');
console.log(msg);
console.log('--------------------------');
};
var messages = {
hi: 'Hey Guys, I am zce~'
};
// 导入模块成员
// 使用模块成员
const msg = messages.hi;
log(msg);
}());
可以看到,rollup的打包结果十分的简洁,基本上就和我们以前手写的代码是一样的。相比于Webpack中大量的引导代码和一堆的模块函数,这里的输出结果几乎没有任何多余的代码,它就是把我们打包过程中各个模块按照模块的依赖顺序先后的拼接到了一起。
而且在输出结果中只会保留那些用到了的部分,对于未引用的部分都没有输出,这是因为Rollup会自动开启Tree-shaking去优化输出结果,Tree-shaking这个概念最早也是在Rollup中提出的。
在项目根目录下创建一个rollup.config.js文件
export default {
input: "src/index.js", // 指定打包入口文件
ouput:{ // 指定输出相关配置
file: "dist/bundle.js", //指定输出的文件名
format: "iife" // 指定输出格式
}
}
执行打包,需要通过–config参数表明我们要使用相关配置文件,默认是不会去读取配置文件的
yarn rollup --config
也可以指定读取的配置文件名称
yarn rollup --config rollup.config.js
Rollup自身的功能就只是ESM模块的合并打包,如果我们项目有更高级的需求,例如我们想要加载其他类型资源模块,或者是我们要在代码中导入CommonJS模块,又或是想要编译ES新特性,这些额外的需求,Rollup同样支持使用扩展的方式去实现,而且插件是Rollup唯一的扩展方式。
例如我们要使用package.json中的数据,就需要配置插件
yarn add rollup-plugin-json --dev
// rollup.config.js
import json from 'rollup-plugin-json' // 这个插件模块默认导出的是一个函数
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
plugins: [
json() // 将函数调用结果放入
]
}
//index.js
import { name, version } from '../package.json'
log(name)
log(version)
打包yarn rollup --config
打包结果如下:
(function () {
'use strict';
const log = msg => {
console.log('---------- INFO ----------');
console.log(msg);
console.log('--------------------------');
};
var name = "03-plugins";
var version = "0.1.0";
log(name);
log(version);
}());
Rollup默认只能按照文件路径去加载本地的文件模块,对于node_modules中那些第三方模块,它并不能像Webpack一样直接以模块名称导入对应模块,为了抹平这样一种差异,Rollup官方给出了一个rollup-plugin-node-resolve插件,通过使用这个插件,我们就能在代码中直接使用模块名称导入对应模块。
安装
yarn add rollup-plugin-node-resolve --dev
配置
import json from 'rollup-plugin-json'
import resolve from 'rollup-plugin-node-resolve'
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
plugins: [
json(),
resolve()
]
}
使用
// 导入模块成员
import _ from 'lodash-es'// 加载提前安装好的lodash的esm版本,因为rollup默认只能处理esm模块,如果我们要使用普通版本,则需要做额外的处理
log(_.camelCase('hello world'))
打包结果
lodash-es中的内容被打包到bundle.js中了。
因为还是有很多模块使用的CommonJS的方式导出成员,所以为了兼容这些模块,官方给出了一个rollup-plugin-commonjs插件。
安装
yarn add rollup-plugin-commonjs --dev
配置
import commonjs from 'rollup-plugin-commonjs'
export default {
...,
plugins: [
commonjs()
]
}
commonjs模块示例
// cjs-module.js
module.exports = {
foo: 'bar'
}
// index.js
import cjs from './cjs-module'
log(cjs)
打包结果
(function () {
'use strict';
var cjsModule = {
foo: 'bar'
};
log(cjsModule);
}());
在Rollup的最新版本中已经支持代码拆分了,我们可以使用符合ESM标准的动态导入的方式去实现模块的按需加载,Rollup内部会自动去处理代码的拆分,也就是分包。
需要修改配置为如下:
export default {
input: 'src/index.js',
output: {
dir: 'dist', // 代码拆分必须要输出多个文件,我们就不能使用file属性了,因为file是指定单个文件输出的文件名,我们要输出多个文件就要用dir属性
format: 'amd' // format格式不能是iife了,因为iife自执行函数会把所有模块放到一个函数当中,它并没有像webpack一样有一些引导代码,所以说它没有办法实现代码拆分,要实现代码拆分就必须要使用amd或者commonjs这样的标准,我们在浏览器中就只能使用amd的标准
}
}
打包入口:
//index.js
import('./logger').then(({ log }) => {
log('code splitting~')
})
打包结果
生成了入口js和动态导入所对应的bundle.js,它们都是采用amd标准输出的。
Rollup同样支持多入口打包,而且对于不同入口的一些公共部分也会自动提取到单个文件当中作为独立的bundle。
配置:
export default {
input: ['src/index.js', 'src/album.js'],
// input: {
// foo: 'src/index.js',
// bar: 'src/album.js'
// },
// 以上两种方式都是可以的
output: {
dir: 'dist',
format: 'amd'// 因为多入口打包内部会自动提取公共模块,也就是说内部会使用代码拆分,所以不能用iife的输出格式
}
}
执行打包,yarn rollup --config
,打包过后dist目录下会多出三个打包文件
其中分别是两个入口的打包文件,和公共部分被提取出来的公共模块。
有一点需要注意,对于amd格式输出的js文件,我们不能直接去引用到页面上,而必须要去通过实现amd标准的库去加载。
我们在dist目录下新建一个index.html文件。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<script src="https://unpkg.com/[email protected]/require.js" data-main="album.js">script>
body>
html>
执行serve dist
启动服务器,可以看到我们的打包结果正常加载进来并进行工作了。
通过以上的探索和尝试,我们发现Rollup确实有它的优势。
但他的缺点也很明显:
综合以上这些特点,我们发现,如果我们正在开发一个应用程序,我们肯定需要大量引入第三方模块,同时我们又需要HMR这样的功能去提升我们的开发体验,而且我们的应用一旦大了过后,还涉及到必须要去分包,这些需求Rollup在满足上都会有一些欠缺。
而如果我们开发的是一个框架或者类库,这些优点就特别有必要,这点缺点几乎都可以忽略,就拿加载第三方模块来说,在我们开发类库的时候,很少会在代码中依赖一些第三方模块,所以很多像React、Vue之类的框架中都是使用Rollup作为模块打包器。
总结来说就是,Webpack大而全,Rollup小而美,对于它们两者之间的选择上,我们基本上就是开发应用程序选择Webpack,开发框架或类库选择Rollup