前言,为什么要学习RollUp?因为vue框架源码就是使用Rollup进行打包的。所以需要学习。而且rollup和webpack相比更适用于框架的开发。打包后的代码可读性更高。
Rollup介绍
- esModule打包器
- 将项目中散落的小模块打包成整块的代码,让划分的小模块可以更好地运行在浏览器环境和nodejs环境
- 作用: 与webpack非常类似
- 对比: Rollup相对于webpack更小巧,webpack可以完成项目中各种工程化的需求,而Rollup仅仅是一款ESM(ESmodule)打包器,没有任何其它的功能。 例如,webpack中有HMR(热替换功能),Rollup中没有
- Rollup的意义: 提供一个充分利用ESM各项特性的高效打包器。利用ESmodule的各种特性构建出结构比较扁平,性能比较出众的类库。
Rollup实操
一、新建项目
新建了一个超简单项目用来学习,项目中代码都是要 esmodule 规范来进行项目模块化,项目目录如下:
rollup
├─ src
├─ ─ log.js
├─ ─ sum.js
├─ ─ index.js
├─ package.json
└─ yarn.lock
//index.js
import { log } from './log.js';
import { sum } from './sum.js';
console.log('hello');
console.log(sum(2, 3));
//log.js
export function log(argu) {
console.log(argu);
}
//sum.js
export function sum(num1, num2) {
return num1 + num2;
}
二、安装Rollup
将Rollup作为开发依赖进行安装。
yarn add rollup --dev
安装完成后,在项目node-modules中,会有rollup.cmd命令。在命令行中可以使用rollup进行打包。
使用yarn命令可以直接去执行node-modules中bin文件夹下的命令,不用到命令目录下。
三、使用
1、尝试执行rollup
yarn rollup
没传递任何参数时会自动打印出帮助信息
use settings in config file
rollup -c
帮助信息提示:应该通过参数指定一个打包入口文件
2、配置打包入口文件
新建一个 index.js 文件
yarn rollup ./src/index
执行完上面命令,发现控制台输出打包内容。输出的内容中混入了所引入的文件的方法等。将引入的方法放在头部,文件自身的代码放至在底部。
接下来我们给rollup设置一个打包后文件的输出格式,来控制以什么格式输出。
3、配置打包后文件输出格式
在打包命令后使用 --format 配置打包格式。
yarn rollup ./src/index.js --format iife
iife为浏览器环境下可用的自执行函数格式
$ H:\xp\rollup\node_modules.bin\rollup ./src/index.js --format iife
./src/index.js → stdout...
(function () {
'use strict';
function log(msg) {
console.log(msg);
}
var message = {
hi: 'hello'
};
function sum(num1, num2) {
return num1 + num2;
}
//index.js
const hi = message.hi;
log(hi);
log(sum(2 + 3));
})();
4、配置打包输出文件路径
yarn rollup ./src/index.js --format iife --file dist/bundle.js
可以使用--file
配置输出文件,打包结果会输出到输出文件中。
5、查看打包后输出的文件
(function () {
'use strict';
function log(msg) {
console.log(msg);
}
var message = {
hi: 'hello'
};
function sum(num1, num2) {
return num1 + num2;
}
//index.js
const hi = message.hi;
log(hi);
log(sum(2 + 3));
})();
查看打包输出的文件 ,打包后输出的结果非常简洁,和我们以前手写的代码是一样的,那相比于webpack当中大量的那些引导代码,还有一堆的模块函数,Rollup打包的输出结果几乎没有任何多余的代码,它就是把我们打包过程当中,各个模块按照模块的依赖顺序,先后的拼接到一起,而且输出结果当中,它只会去保留那些用到的部分。对未引用的部分都没有输出,这是因为Rollup默认会自动开启treeshaking,treeshaking最早也是在Rollup这个工具中提出的。
Rollup配置文件
Rollup同样支持以配置文件的方式去配置打包过程中的各项参数
一、项目根目录下新建配置文件
在项目的根目录下,新建一个rollup.config.js
的配置文件,这个配置文件是同样运行在Node的环境当中,不过Rollup它自身会额外处理这个配置文件,所以这里我们可以直接使用ESmodule。文件中需要导出一个配置对象,在这个对象中我们可以去通过
- input属性去指定我们打包的入口文件路径
- output指定输出的相关配置,output格式要求是一个对象
-
- 在output的对象当中,使用
file
属性去指定输出的文件名 - 在output的对象当中,使用
format
属性用来去指定我们的输出格式。
- 在output的对象当中,使用
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
}
};
执行rollup命令,命令需要通过--config
表明使用项目中配置文件,默认不会读取配置文件
yarn rollup --config
在项目中可以针对开发环境、生产环境使用不同的配置文件,例如:
yarn rollup --config rollup.dev.config.js
yarn rollup --config rollup.pro.config.js
Rollup使用插件
Rollup自身的功能仅仅只是esmodel模块的合并打包,那如果我们的项目有更高级的需求,例如
- 想要去加载其它类型的资源文件
- 想要在代码当中去导入commonjs模块
- 想要Rollup去帮我们编译EMCAScript的新特性 Rollup支持使用插件的方式去扩展实现这些额外的需求,而且插件的是Rollup唯一的扩展方式,它不像webpack中划分了loader,plugin和Minimizer三种扩展方式。
rollup-plugin-json 插件
安装并导入rollup-plugin-json 插件,可以在项目中引入并使用json文件。如下示例中,安装插件后,在rollup配置文件中引入包,在index.js中可以引入 package.json 文件中的值。
一、如何在Rollup中使用插件
1、安装 rollup-plugin-json 作为开发依赖
yarn add rollup-plugin-json --dev
2、使用import
的方式去导入这个插件模块。
将这个函数的调用结果添加到我们配对对象的plugins数组当中,那需要注意的是,这里我们是将调用的结果放到数组当中,而不是直接将这个函数放进去。
import json from 'rollup-plugin-json'
export default{
input:'src/index.js',
output:{
file:'dist/bundle.js',
format:''
},
plugins:[
json()
]
}
3、在项目文件中尝试使用目前引入的插件。打开项目 src/index.js 文件
//src/index.js
import {name,version} from '../package.json'
console.log(name)
console.log(version)
执行打包命令,运行打包:
yarn rollup --config
那打包完后后,我们找到输出的bundle.js
,那此时你就能够看到package.json
当中的name和version已经被正常打包进来了,而json当中那些没有用到的属性,也都会被Tree shaking 掉。
(function () {
'use strict';
function log(msg) {
console.log(msg);
}
var message = {
hi: 'hello'
};
function sum(num1, num2) {
return num1 + num2;
}
var name = "rollup";
//index.js
const hi = message.hi;
console.log(name);
log(hi);
log(sum(2 + 3));
})();
Rollup加载npm模块
Rollup默认只能够按照文件路径方式去加载本地的文件模块,那对于nodd_module
当中那些第三方的模块,它并不能够像webpack一样,直接去通过模块的名称导入对应的模块,那为了抹平这样一个差异,Rollup官方给出了一个rollup-plugin-node-resolve
这样的插件,那通过使用这个插件,我们可以在代码当中直接去使用模块名称导入对应的模块。
1、安装插件rollup-plugin-node-resolve
yarn add rollup-plugin-node-resolve --dev
2、修改配置文件
打开配置文件,导入插件,然后将这个插件函数的调用结果配置到plugin子数组中
// rollup.config.js
import json from 'rollup-plugin-json';
import npm from 'rollup-plugin-node-resolve';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
plugins: [json(), npm()]
};
3、安装第三方模块
这里安装lodash为例
yarn add lodash
4、导入lodash的ESmodule版本
//index.js
import { log } from './log.js';
import message from './message';
import { sum } from './sum.js';
import { name, version } from '../package.json';
// 使用刚安装好的lodash模块
import _ from 'lodash-es';
const hi = message.hi;
let arr1 = _.first([2, 3, 4]);
console.log(name);
log(hi);
log(sum(2 + 3));
导入过后呢,我们就可以使用这个模块所提供的一些工具方法了
4、打开命令行终端,运行rollup进行打包
yarn rollup --config
lodash中的代码就能够被打包到我们的boundle.js中了,这里引入lodash的ESmodule版本的原因是因为rollup它默认只能够ESmodule模块,如果说需要去使用普通版本,需要做额外的处理。
//dist/bundle.js
(function (_) {
'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
function log(msg) {
console.log(msg);
}
var message = {
hi: 'hello'
};
function sum(num1, num2) {
return num1 + num2;
}
var name = "rollup";
//index.js
const hi = message.hi;
___default["default"].first([2, 3, 4]);
console.log(name);
log(hi);
log(sum(2 + 3));
})(_);
Rollup加载CommonJS模块
Rollup设计的就是只处理ESmodule模块打包,那如果我们在代码当中去导入commonJSl模块,默认是不被支持的,但是目前还是会有大量的NPM模块使用commonJS方式去导出成员,所以为了兼容这些模块,官方又给出了一个rollup-plugin-commonjs
插件。
使用方法
一、安装rollup-plugin-commonjs
插件
yarn add rollup-plugin-commonjs --dev
二、修改配置文件
打开rollup打包配置文件,导入rollup-plugin-commonjs插件,在pliguns数组中加入插件函数的执行结果。
// rollup.config.js
import json from 'rollup-plugin-json';
import npm from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
plugins: [json(), npm(), commonjs()]
};
三、插件CommonJs模块文件并使用
目录中创建 cjs-module.js
文件,项目中添加一个Commonjs模块文件。
\
//cjs-module.js
module.exports={
foo:'bar'
}
在index.js中导入这个模块,
//index.js
import { log } from './log.js';
import message from './message';
import { sum } from './sum.js';
import { name, version } from '../package.json';
// 使用刚安装好的lodash模块
import _ from 'lodash-es';
// 引入commonjs模块.commonJs模块默认为以export.default形式导出
import cjs from './cjs-module';
const hi = message.hi;
let arr1 = _.first([2, 3, 4]);
log(name);
log(cjs);
log(hi);
log(sum(2 + 3));
四、执行rollup打包
yarn rollup --config
(function (_) {
'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
function log(msg) {
console.log(msg);
}
var message = {
hi: 'hello'
};
function sum(num1, num2) {
return num1 + num2;
}
var name = "rollup";
//cjs-module.js
var cjsModule = {
foo: 'bar'
};
cjsModule.foo;
//index.js
const hi = message.hi;
___default["default"].first([2, 3, 4]);
log(name);
log(cjsModule);
log(hi);
log(sum(2 + 3));
})(_);
Rollup 代码拆分(Code Splitting)
在Rollup最新的版本中,已经开始支持代码拆分了,那么同样可以使用符合ESmodule标准的动态导入的方式去实现模块的按需加载,Rollup内部也会自动去处理代码的拆分(也就是分包)。
一、回到打包入口文件当中
//index.js
// import动态导入方法返回一个promise。then方法中可以取到模块导入后的对象。这里解构提取出 log方法
import('./log').then(({ log }) => {
log('hello');
});
然后我们使用动态导入方式去导入一下 logger.js
对应的这个模块,import()
方法返回的是一个promise对象,在它的.then( )
方法中我们可以拿到一个模块导入过后的对象,模块导出的那些成员都会放在这对象当中,所以可以使用解构的方式去提取出来里面的log
方法,这里可以调用log()
方法,
尝试运行 rollup 进行打包
yarn rollop --config
src/index2.js → dist/bundle.js...
[!] Error: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
https://rollupjs.org/guide/en/#outputformat
Error: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
at error (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:158:30)
at validateOptionsForMultiChunkOutput (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:16109:16)
at Bundle.generate (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:15948:17)
at async handleGenerateWrite (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:23480:23)
at async Promise.all (index 0)
at async build (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1576:5)
at async runRollup (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1730:21)
error Command failed with exit code 1.
会报出一个错误,说的是我们使用Code Splitting的输出格式,不能是IIFE自执行函数这种形式,原因很简单,因为自执行函数,它会把所有的模块都放到同一个函数当中,它并没有像webpack一样有一些引导代码,所以说它没有办法实现代码拆分,那想使用代码拆分的话,就必须要使用AMD或者是Commonjs这样的一些其它的标准,那我们在浏览器还当中,我们只能使用AMD标准,所以我们这里需要使用amd的格式去输出打包结果
重新执行打包命令,修改打包文件输出格式,通过--format
参数去覆盖配置文件当中的format设置,把它设置为amd。这里在命令中使用--format amd 覆盖配置文件中iife 打包格式。
yarn rollop --config --format amd
这里同样报出了一个错误,说的是我们Code Splitting
这种方式,它需要输出多个文件,因为需要输出多个文件,我们这儿呢就不能再使用file
的这种配置方式,因为file
它是指定我们一个单子文件输出的文件的文件名,那我们如果需要输出多个文件的话,我们可以使用dir
的参数
$ H:\xp\rollup\node_modules.bin\rollup --config --format amd
src/index2.js → dist/bundle.js...
[!] Error: Invalid value for option "output.file" - when building multiple chunks, the "output.dir" option must be used, not "output.file". To inline dynamic imports, set
the "inlineDynamicImports" option.
https://rollupjs.org/guide/en/#outputdir
Error: Invalid value for option "output.file" - when building multiple chunks, the "output.dir" option must be used, not "output.file". To inline dynamic imports, set the
"inlineDynamicImports" option.
at error (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:158:30)
at validateOptionsForMultiChunkOutput (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:16111:16)
at Bundle.generate (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:15948:17)
at async handleGenerateWrite (H:\xp\rollup\node_modules\rollup\dist\shared\rollup.js:23480:23)
at async Promise.all (index 0)
at async build (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1576:5)
at async runRollup (H:\xp\rollup\node_modules\rollup\dist\bin\rollup:1730:21)
error Command failed with exit code 1.
修改rollup打包配置文件
export default{
input:'src/index.js',
output:{
//file:'dist/bundle.js',
//format:'IIFE'
dir:'dist',
format:'amd'
}
}
执行打包命令,在执行打包命令前删除dist文件夹下所有文件,等下观察分包打包后输出文件
yarn rollup --config
dist 文件夹下成两个文件。
//index.js
define(['require'], (function (require) { 'use strict';
// import动态导入方法返回一个promise。then方法中可以取到模块导入后的对象。这里解构提取出 log方法
new Promise(function (resolve, reject) { require(['./log-ace3f028'], resolve, reject); }).then(({ log }) => {
log('hello');
});
}));
//log-ace3f028.js
define(['exports'], (function (exports) { 'use strict';
function log(msg) {
console.log(msg);
}
function error(msg) {
console.error(msg);
}
exports.error = error;
exports.log = log;
}));
Rollup 多入口打包
支持多入口打包。
对于不同入口于文件中公共部分也会自动提取到单个文件中作为bundle。
配置多入口,将input格式设置为数组,放入多个打包入口文件。
export default{
input:['src/index.js','src/']
output:{
//file:'dist/bundle.js',
//format:'IIFE'
dir:'dist',
format:'amd'
}
}
不能使用IIFE输出格式,需要将输出格式改为amd
执行打包,打包后会生成,不同的入口文件,及公共提取的公共模块
不能直接用到页面上,需要使用amd的库加载。
使用requireJs引入amd模块
<script src='' data-main=''></script>
Rollup和webpack如何选
- 输出结果更加扁平,效率高
- 自动移除未引用代码,tree shaking
- 打包结果依然完全可读
缺点:
- 加载非ESM的第三方模块比较复杂\
- 模块最终都被打包到一个函数中,无法实现HMR\
- 浏览器环境中,代码拆分功能依赖AMD库
如果我们正在开发应用程序,rollup欠缺。
如果我们正在开发一个框架或者类库,大多数知名框架/库都在使用Rollup。