一篇文章带你从零快速上手Rollup
前言
项目中一直用的都是webpack,前一段需要开发几个类库供其他平台使用,本来打算继续用webpack的,但感觉webpack用来开发js库,不仅繁琐而且打包后的文件体积也比较大。正好之前看vue源码,知道vue也是通过rollup打包的。这次又是开发类库的,于是就快速上手了rollup。
本篇文章是我有了一定的项目实践后,回过来给大家分享一下如何从零快速上手rollup。
什么是rollup?
系统的了解rollup之前,我们先来简单了解下Whatisrollup?
关于rollup的介绍,官方文档已经写的很清楚了:
Rollup是一个JavaScript模块打包器,可以将小块代码编译成大块复杂的代码,例如library或应用程序。
与Webpack偏向于应用打包的定位不同,rollup.js更专注于Javascript类库打包。
我们熟知的Vue、React等诸多知名框架或类库都是通过rollup.js进行打包的。
为什么是rollup?
webpack我相信做前端的同学大家都用过,那么为什么有些场景还要使用rollup呢?这里我简单对webpack和rollup做一个比较:
总体来说webpack和rollup在不同场景下,都能发挥自身优势作用。webpack对于代码分割和静态资源导入有着“先天优势”,并且支持热模块替换(HMR),而rollup并不支持。
所以当开发应用时可以优先选择webpack,但是rollup对于代码的Tree-shaking和ES6模块有着算法优势上的支持,若你项目只需要打包出一个简单的bundle包,并是基于ES6模块开发的,可以考虑使用rollup。
其实webpack从2.0开始就已经支持Tree-shaking,并在使用babel-loader的情况下还可以支持es6module的打包。实际上,rollup已经在渐渐地失去了当初的优势了。但是它并没有被抛弃,反而因其简单的API、使用方式被许多库开发者青睐,如React、Vue等,都是使用rollup作为构建工具的。
快速上手
我们先花大概十分钟左右的时间来了解下rollup的基本使用以及完成一个helloworld。
安装
首先全局安装rollup:
npmirollup-g
目录准备(helloworld)
接着,我们初始化一个如下所示的项目目录
├──dist#编译结果
├──example#HTML引用例子
│ └──index.html
├──package.json
└──src#源码
└──index.js
首先我们在src/index.js中写入如下代码:
console.log("柯森");
然后在命令行执行以下命令:
rollupsrc/index.js-fumd-odist/bundle.js
执行命令,我们即可在dist目录下生成bundle.js文件:
(function(factory){
typeofdefine==='function'&&define.amd?define(factory):
factory();
}((function(){'usestrict';
console.log("柯森");
})));
这时,我们再在example/index.html中引入上面打包生成的bundle.js文件,打开浏览器:
如我们所预料的,控制台输出了柯森。
到这里,我们就用rollup打包了一个最最简单的demo。
可能很多同学看到这里对于上面命令行中的参数不是很明白,我依次说明下:
- -f。-f参数是--format的缩写,它表示生成代码的格式,amd表示采用AMD标准,cjs为CommonJS标准,esm(或es)为ES模块标准。-f的值可以为amd、cjs、system、esm('es'也可以)、iife或umd中的任何一个。
- -o。-o指定了输出的路径,这里我们将打包后的文件输出到dist目录下的bundle.js
其实除了这两个,还有很多其他常用的命令(这里我暂且列举剩下两个也比较常用的,完整的rollup命令行参数):
- -c。指定rollup的配置文件。
- -w。监听源文件是否有改动,如果有改动,重新打包。
使用配置文件(rollup.config.js)
使用命令行的方式,如果选项少没什么问题,但是如果添加更多的选项,这种命令行的方式就显得麻烦了。
为此,我们可以创建配置文件来囊括所需的选项
在项目中创建一个名为rollup.config.js的文件,增加如下代码:
exportdefault{
input:["./src/index.js"],
output:{
file:"./dist/bundle.js",
format:"umd",
name:"experience",
},
};
然后命令行执行:
rollup-c
打开dist/bundle.js文件,我们会发现和上面采用命令行的方式打包出来的结果是一样的。
这里,我对配置文件的选项做下简单的说明:
- input表示入口文件的路径(老版本为entry,已经废弃)
- output表示输出文件的内容,它允许传入一个对象或一个数组,当为数组时,依次输出多个文件,它包含以下内容:
- output.file:输出文件的路径(老版本为dest,已经废弃)
- output.format:输出文件的格式
- output.banner:文件头部添加的内容
- output.footer:文件末尾添加的内容
到这里,相信你已经差不多上手rollup了。
进阶
但是,这对于真实的业务场景是远远不够的。
下面,我将介绍rollup中的几种常用的插件以及external属性、tree-shaking机制。
resolve插件
为什么要使用resolve插件
在上面的入门案例中,我们打包的对象是本地的js代码和库,但实际开发中,不太可能所有的库都位于本地,我们大多会通过npm下载远程的库。
与webpack和browserify这样的其他捆绑包不同,rollup不知道如何打破常规去处理这些依赖。因此我们需要添加一些配置。
resolve插件使用
首先在我们的项目中添加一个依赖the-answer,然后修改src/index.js文件:
importanswerfrom"the-answer";
exportdefaultfunction(){
console.log("theansweris"+answer);
}
执行npmrunbuild。
这里为了方便,我将原本的rollup-c-w添加到了package.json的scripts中:"build":"rollup-c-w"
会得到以下报错:
打包后的bundle.js仍然会在Node.js中工作,但是the-answer不包含在包中。为了解决这个问题,将我们编写的源码与依赖的第三方库进行合并,rollup.js为我们提供了resolve插件。
首先,安装resolve插件:
npmi-D@rollup/plugin-node-resolve
修改配置文件rollup.config.js:
importresolvefrom"@rollup/plugin-node-resolve";
exportdefault{
input:["./src/index.js"],
output:{
file:"./dist/bundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve()],
};
这时再次执行npmrunbuild,可以发现报错已经没有了:
打开dist/bundle.js文件:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
varindex=42;
functionindex$1(){
console.log("theansweris"+index);
}
returnindex$1;
})));
打包文件bundle.js中已经包含了引用的模块。
有些场景下,虽然我们使用了resolve插件,但可能我们仍然想要某些库保持外部引用状态,这时我们就需要使用external属性,来告诉rollup.js哪些是外部的类库。
external属性
修改rollup.js的配置文件:
importresolvefrom"@rollup/plugin-node-resolve";
exportdefault{
input:["./src/index.js"],
output:{
file:"./dist/bundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve()],
external:["the-answer"],
};
重新打包,打开dist/bundle.js文件:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory(require('the-answer')):
typeofdefine==='function'&&define.amd?define(['the-answer'],factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory(global.answer));
}(this,(function(answer){'usestrict';
function_interopDefaultLegacy(e){returne&&typeofe==='object'&&'default'ine?e:{'default':e};}
varanswer__default=/*#__PURE__*/_interopDefaultLegacy(answer);
functionindex(){
console.log("theansweris"+answer__default['default']);
}
returnindex;
})));
这时我们看到the-answer已经是做为外部库被引入了。
commonjs插件
为什么需要commonjs插件
rollup.js编译源码中的模块引用默认只支持ES6+的模块方式import/export。然而大量的npm模块是基于CommonJS模块方式,这就导致了大量npm模块不能直接编译使用。
因此使得rollup.js编译支持npm模块和CommonJS模块方式的插件就应运而生:@rollup/plugin-commonjs。
commonjs插件使用
首先,安装该模块:
npmi-D@rollup/plugin-commonjs
然后修改rollup.config.js文件:
importresolvefrom"@rollup/plugin-node-resolve";
importcommonjsfrom"@rollup/plugin-commonjs";
exportdefault{
input:["./src/index.js"],
output:{
file:"./dist/bundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve(),commonjs()],
external:["the-answer"],
};
babel插件
为什么需要babel插件?
我们在src目录下添加es6.js文件(⚠️这里我们使用了es6中的箭头函数):
consta=1;
constb=2;
console.log(a,b);
exportdefault()=>{
returna+b;
};
然后修改rollup.config.js配置文件:
importresolvefrom"@rollup/plugin-node-resolve";
importcommonjsfrom"@rollup/plugin-commonjs";
exportdefault{
input:["./src/es6.js"],
output:{
file:"./dist/esBundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve(),commonjs()],
external:["the-answer"],
};
执行打包,可以看到dist/esBundle.js文件内容如下:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
consta=1;
constb=2;
console.log(a,b);
vares6=()=>{
returna+b;
};
returnes6;
})));
可以看到箭头函数被保留下来,这样的代码在不支持ES6的环境下将无法运行。我们期望在rollup.js打包的过程中就能使用babel完成代码转换,因此我们需要babel插件。
babel插件的使用
首先,安装:
npmi-D@rollup/plugin-babel
同样修改配置文件rollup.config.js:
importresolvefrom"@rollup/plugin-node-resolve";
importcommonjsfrom"@rollup/plugin-commonjs";
importbabelfrom"@rollup/plugin-babel";
exportdefault{
input:["./src/es6.js"],
output:{
file:"./dist/esBundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve(),commonjs(),babel()],
external:["the-answer"],
};
然后打包,发现会出现报错:
提示我们缺少@babel/core,因为@babel/core是babel的核心。我们来进行安装:
npmi@babel/core
再次执行打包,发现这次没有报错了,但是我们尝试打开dist/esBundle.js:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
consta=1;
constb=2;
console.log(a,b);
vares6=(()=>{
returna+b;
});
returnes6;
})));
可以发现箭头函数仍然存在,显然这是不正确的,说明我们的babel插件没有起到作用。这是为什么呢?
原因是由于我们缺少.babelrc文件,添加该文件:
{
"presets":[
[
"@babel/preset-env",
{
"modules":false,
//"useBuiltIns":"usage"
}
]
]
}
我们看.babelrc配置了presetenv,所以先安装这个插件:
npmi@babel/preset-env
这次再次执行打包,我们打开dist/esBundle.js文件:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
vara=1;
varb=2;
console.log(a,b);
vares6=(function(){
returna+b;
});
returnes6;
})));
可以看到箭头函数被转换为了function,说明babel插件正常工作。
json插件
为什么要使用json插件?
在src目录下创建json.js文件:
importjsonfrom"../package.json"; console.log(json.author);
内容很简单,就是引入package.json,然后去打印author字段。
修改rollup.config.js配置文件:
importresolvefrom"@rollup/plugin-node-resolve";
importcommonjsfrom"@rollup/plugin-commonjs";
importbabelfrom"@rollup/plugin-babel";
exportdefault{
input:["./src/json.js"],
output:{
file:"./dist/jsonBundle.js",
format:"umd",
name:"experience",
},
plugins:[resolve(),commonjs(),babel()],
external:["the-answer"],
};
执行打包,发现会发生如下报错:
提示我们缺少@rollup/plugin-json插件来支持json文件。
json插件的使用
来安装该插件:
npmi-D@rollup/plugin-json
同样修改下配置文件,将插件加入plugins数组即可。
然后再次打包,发现打包成功了,我们打开生成的dist/jsonBundle目录:
(function(factory){
typeofdefine==='function'&&define.amd?define(factory):
factory();
}((function(){'usestrict';
varname="rollup-experience";
varversion="1.0.0";
vardescription="";
varmain="index.js";
vardirectories={
example:"example"
};
varscripts={
build:"rollup-c-w",
test:"echo\"Error:notestspecified\"&&exit1"
};
varauthor="Cosen";
varlicense="ISC";
vardependencies={
"@babel/core":"^7.11.6",
"@babel/preset-env":"^7.11.5",
"the-answer":"^1.0.0"
};
vardevDependencies={
"@rollup/plugin-babel":"^5.2.0",
"@rollup/plugin-commonjs":"^15.0.0",
"@rollup/plugin-json":"^4.1.0",
"@rollup/plugin-node-resolve":"^9.0.0"
};
varjson={
name:name,
version:version,
description:description,
main:main,
directories:directories,
scripts:scripts,
author:author,
license:license,
dependencies:dependencies,
devDependencies:devDependencies
};
console.log(json.author);
})));
完美!!
tree-shaking机制
这里我们以最开始的src/index.js为例进行说明:
importanswerfrom"the-answer";
exportdefaultfunction(){
console.log("theansweris"+answer);
}
修改上述文件:
consta=1;
constb=2;
exportdefaultfunction(){
console.log(a+b);
}
执行打包。打开dist/bundle.js文件:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
vara=1;
varb=2;
functionindex(){
console.log(a+b);
}
returnindex;
})));
再次修改src/index.js文件:
consta=1;
constb=2;
exportdefaultfunction(){
console.log(a);
}
再次执行打包,打开打包文件:
(function(global,factory){
typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():
typeofdefine==='function'&&define.amd?define(factory):
(global=typeofglobalThis!=='undefined'?globalThis:global||self,global.experience=factory());
}(this,(function(){'usestrict';
vara=1;
functionindex(){
console.log(a);
}
returnindex;
})));
发现了什么?
我们发现关于变量b的定义没有了,因为源码中并没有用到这个变量。这就是ES模块著名的tree-shaking机制,它动态地清除没有被使用过的代码,使得代码更加精简,从而可以使得我们的类库获得更快的加载速度。
总结
本文大致向大家介绍了什么是rollup以及如何快速上手rollup。文中提到的这些其实只是冰山一角,rollup能玩的东西还有很多,关于更多可以去rollup官网查询
到此这篇带你从零快速上手Rollup的文章就介绍到这了,更多相关从零快速上手Rollup内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。