一篇文章带你从零快速上手Rollup


Posted in Javascript onSeptember 07, 2020

前言

项目中一直用的都是webpack,前一段需要开发几个类库供其他平台使用,本来打算继续用webpack的,但感觉webpack用来开发js库,不仅繁琐而且打包后的文件体积也比较大。正好之前看vue源码,知道vue也是通过rollup打包的。这次又是开发类库的,于是就快速上手了rollup。

本篇文章是我有了一定的项目实践后,回过来给大家分享一下如何从零快速上手rollup。

什么是rollup?

系统的了解rollup之前,我们先来简单了解下What is rollup?

关于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的情况下还可以支持es6 module的打包。实际上,rollup已经在渐渐地失去了当初的优势了。但是它并没有被抛弃,反而因其简单的API、使用方式被许多库开发者青睐,如React、Vue等,都是使用rollup作为构建工具的。

快速上手

我们先花大概十分钟左右的时间来了解下rollup的基本使用以及完成一个hello world。

安装

首先全局安装rollup:

npm i rollup -g

目录准备(hello world)

接着,我们初始化一个如下所示的项目目录

├── dist # 编译结果
├── example # HTML引用例子
│   └── index.html
├── package.json
└── src # 源码
    └── index.js

首先我们在src/index.js中写入如下代码:

console.log("柯森");

然后在命令行执行以下命令:

rollup src/index.js -f umd -o dist/bundle.js

执行命令,我们即可在dist目录下生成bundle.js文件:

(function (factory) {
 typeof define === 'function' && define.amd ? define(factory) :
 factory();
}((function () { 'use strict';

 console.log("柯森");

})));

这时,我们再在example/index.html中引入上面打包生成的bundle.js文件,打开浏览器:

一篇文章带你从零快速上手Rollup

如我们所预料的,控制台输出了柯森。

到这里,我们就用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的文件,增加如下代码:

export default {
 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文件:

import answer from "the-answer";

export default function () {
 console.log("the answer is " + answer);
}

执行npm run build

这里为了方便,我将原本的rollup -c -w添加到了package.json的scripts中:"build": "rollup -c -w"

会得到以下报错:

一篇文章带你从零快速上手Rollup

打包后的bundle.js仍然会在Node.js中工作,但是the-answer不包含在包中。为了解决这个问题,将我们编写的源码与依赖的第三方库进行合并,rollup.js为我们提供了resolve插件。

首先,安装resolve插件:

npm i -D @rollup/plugin-node-resolve

修改配置文件rollup.config.js:

import resolve from "@rollup/plugin-node-resolve";

export default {
 input: ["./src/index.js"],
 output: {
 file: "./dist/bundle.js",
 format: "umd",
 name: "experience",
 },
 plugins: [resolve()],
};

这时再次执行npm run build,可以发现报错已经没有了:

一篇文章带你从零快速上手Rollup

打开dist/bundle.js文件:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 var index = 42;

 function index$1 () {
 console.log("the answer is " + index);
 }

 return index$1;

})));

打包文件bundle.js中已经包含了引用的模块。

有些场景下,虽然我们使用了resolve插件,但可能我们仍然想要某些库保持外部引用状态,这时我们就需要使用external属性,来告诉rollup.js哪些是外部的类库。

external 属性

修改rollup.js的配置文件:

import resolve from "@rollup/plugin-node-resolve";

export default {
 input: ["./src/index.js"],
 output: {
 file: "./dist/bundle.js",
 format: "umd",
 name: "experience",
 },
 plugins: [resolve()],
 external: ["the-answer"],
};

重新打包,打开dist/bundle.js文件:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('the-answer')) :
 typeof define === 'function' && define.amd ? define(['the-answer'], factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory(global.answer));
}(this, (function (answer) { 'use strict';

 function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

 var answer__default = /*#__PURE__*/_interopDefaultLegacy(answer);

 function index () {
 console.log("the answer is " + answer__default['default']);
 }

 return index;

})));

这时我们看到the-answer已经是做为外部库被引入了。

commonjs插件

为什么需要commonjs插件

rollup.js编译源码中的模块引用默认只支持 ES6+的模块方式import/export。然而大量的npm模块是基于CommonJS模块方式,这就导致了大量 npm模块不能直接编译使用。

因此使得rollup.js编译支持npm模块和CommonJS模块方式的插件就应运而生:@rollup/plugin-commonjs。

commonjs插件使用

首先,安装该模块:

npm i -D @rollup/plugin-commonjs

然后修改rollup.config.js文件:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
 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 中的箭头函数):

const a = 1;
const b = 2;
console.log(a, b);
export default () => {
 return a + b;
};

然后修改rollup.config.js配置文件:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
 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) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 const a = 1;
 const b = 2;
 console.log(a, b);
 var es6 = () => {
 return a + b;
 };

 return es6;

})));

可以看到箭头函数被保留下来,这样的代码在不支持ES6的环境下将无法运行。我们期望在rollup.js打包的过程中就能使用babel完成代码转换,因此我们需要babel插件。

babel插件的使用

首先,安装:

npm i -D @rollup/plugin-babel

同样修改配置文件rollup.config.js:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";

export default {
 input: ["./src/es6.js"],
 output: {
 file: "./dist/esBundle.js",
 format: "umd",
 name: "experience",
 },
 plugins: [resolve(), commonjs(), babel()],
 external: ["the-answer"],
};

然后打包,发现会出现报错:

一篇文章带你从零快速上手Rollup

提示我们缺少@babel/core,因为@babel/core是babel的核心。我们来进行安装:

npm i @babel/core

再次执行打包,发现这次没有报错了,但是我们尝试打开dist/esBundle.js:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 const a = 1;
 const b = 2;
 console.log(a, b);
 var es6 = (() => {
 return a + b;
 });

 return es6;

})));

可以发现箭头函数仍然存在,显然这是不正确的,说明我们的babel插件没有起到作用。这是为什么呢?

原因是由于我们缺少.babelrc文件,添加该文件:

{
 "presets": [
 [
  "@babel/preset-env",
  {
  "modules": false,
  // "useBuiltIns": "usage"
  }
 ]
 ]
}

我们看.babelrc配置了preset env,所以先安装这个插件:

npm i @babel/preset-env

这次再次执行打包,我们打开dist/esBundle.js文件:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 var a = 1;
 var b = 2;
 console.log(a, b);
 var es6 = (function () {
 return a + b;
 });

 return es6;

})));

可以看到箭头函数被转换为了function,说明babel插件正常工作。

json插件

为什么要使用json插件?

在src目录下创建json.js文件:

import json from "../package.json";
console.log(json.author);

内容很简单,就是引入package.json,然后去打印author字段。

修改rollup.config.js配置文件:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";

export default {
 input: ["./src/json.js"],
 output: {
 file: "./dist/jsonBundle.js",
 format: "umd",
 name: "experience",
 },
 plugins: [resolve(), commonjs(), babel()],
 external: ["the-answer"],
};

执行打包,发现会发生如下报错:

一篇文章带你从零快速上手Rollup

提示我们缺少@rollup/plugin-json插件来支持json文件。

json插件的使用

来安装该插件:

npm i -D @rollup/plugin-json

同样修改下配置文件,将插件加入plugins数组即可。

然后再次打包,发现打包成功了,我们打开生成的dist/jsonBundle目录:

(function (factory) {
 typeof define === 'function' && define.amd ? define(factory) :
 factory();
}((function () { 'use strict';

 var name = "rollup-experience";
 var version = "1.0.0";
 var description = "";
 var main = "index.js";
 var directories = {
 example: "example"
 };
 var scripts = {
 build: "rollup -c -w",
 test: "echo \"Error: no test specified\" && exit 1"
 };
 var author = "Cosen";
 var license = "ISC";
 var dependencies = {
 "@babel/core": "^7.11.6",
 "@babel/preset-env": "^7.11.5",
 "the-answer": "^1.0.0"
 };
 var devDependencies = {
 "@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"
 };
 var json = {
 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为例进行说明:

import answer from "the-answer";

export default function () {
 console.log("the answer is " + answer);
}

修改上述文件:

const a = 1;
const b = 2;
export default function () {
 console.log(a + b);
}

执行打包。打开dist/bundle.js文件:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 var a = 1;
 var b = 2;
 function index () {
 console.log(a + b);
 }

 return index;

})));

再次修改src/index.js文件:

const a = 1;
const b = 2;
export default function () {
 console.log(a);
}

再次执行打包,打开打包文件:

(function (global, factory) {
 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
 typeof define === 'function' && define.amd ? define(factory) :
 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.experience = factory());
}(this, (function () { 'use strict';

 var a = 1;
 function index () {
 console.log(a);
 }

 return index;

})));

发现了什么?

我们发现关于变量b的定义没有了,因为源码中并没有用到这个变量。这就是ES模块著名的tree-shaking机制,它动态地清除没有被使用过的代码,使得代码更加精简,从而可以使得我们的类库获得更快的加载速度。

总结

本文大致向大家介绍了什么是rollup以及如何快速上手rollup。文中提到的这些其实只是冰山一角,rollup能玩的东西还有很多,关于更多可以去rollup 官网查询

到此这篇带你从零快速上手Rollup的文章就介绍到这了,更多相关从零快速上手Rollup内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
JS加ASP二级域名转向的代码
May 17 Javascript
实现变速回到顶部的JavaScript代码
May 09 Javascript
js实现快速分享功能(你的文章分享工具)
Jun 25 Javascript
js如何判断不同系统的浏览器类型
Oct 28 Javascript
JavaScript实现的双向跨域插件分享
Jan 31 Javascript
用JS生成UUID的方法实例
Mar 30 Javascript
原生Javascript插件开发实践
Jan 09 Javascript
jQuery实现简单的计时器功能实例分析
Aug 29 jQuery
Cocos2d实现刮刮卡效果
Dec 20 Javascript
JQuery获取可视区尺寸和文档尺寸及制作悬浮菜单示例
May 14 jQuery
vue中js判断长时间不操作界面自动退出登录(推荐)
Jan 22 Javascript
Nuxt.js nuxt-link与router-link的区别说明
Nov 06 Javascript
基于vue hash模式微信分享#号的解决
Sep 07 #Javascript
在项目vue中使用echarts的操作步骤
Sep 07 #Javascript
解决vue加scoped后就无法修改vant的UI组件的样式问题
Sep 07 #Javascript
Vue.js原理分析之nextTick实现详解
Sep 07 #Javascript
小程序实现可拖动的悬浮按钮
Sep 07 #Javascript
vue 修改 data 数据问题并实时显示操作
Sep 07 #Javascript
nginx部署多个vue项目的方法示例
Sep 06 #Javascript
You might like
PHP 开发环境配置(Zend Server安装)
2010/04/28 PHP
PHP中使用mktime获取时间戳的一个黑色幽默分析
2012/05/31 PHP
PHP扩展迁移为PHP7扩展兼容性问题记录
2016/02/15 PHP
基于PHP实现堆排序原理及实例详解
2020/06/19 PHP
用js统计用户下载网页所需时间的脚本
2008/10/15 Javascript
2010年最佳jQuery插件整理
2010/12/06 Javascript
javascript 三种方法实现获得和设置以及移除元素属性
2013/03/20 Javascript
适用于javascript开发者的Processing.js入门教程
2016/02/24 Javascript
JS上传组件FileUpload自定义模板的使用方法
2016/05/10 Javascript
[原创]Javascript 实现广告后加载 可加载百度谷歌联盟广告
2016/05/11 Javascript
Angular2内置指令NgFor和NgIf详解
2016/08/03 Javascript
jquery移除了live()、die(),新版事件绑定on()、off()的方法
2016/10/26 Javascript
Form表单按回车自动提交表单的实现方法
2016/11/18 Javascript
BootStrap中
2016/12/10 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
认识less和webstrom的less配置方法
2017/08/02 Javascript
基于vue如何发布一个npm包的方法步骤
2019/05/15 Javascript
[00:15]TI9地铁玩家打卡
2019/08/11 DOTA
详解Python中的条件判断语句
2015/05/14 Python
python使用in操作符时元组和数组的区别分析
2015/05/19 Python
Python读取Excel的方法实例分析
2015/07/11 Python
python3如何将docx转换成pdf文件
2018/03/23 Python
python爬虫获取百度首页内容教学
2018/12/23 Python
jupyter lab的目录调整及设置默认浏览器为chrome的方法
2020/04/10 Python
pytorch实现查看当前学习率
2020/06/24 Python
html5教程调用绘图api画简单的圆形代码分享
2013/12/04 HTML / CSS
HTML5里的placeholder属性使用实例和美化显示效果的方法
2014/04/23 HTML / CSS
肯尼亚网上商城:Kilimall
2016/08/20 全球购物
资深地理教师自我评价
2013/09/21 职场文书
北体毕业生求职信
2014/02/28 职场文书
小学优秀班主任材料
2014/12/17 职场文书
先进个人主要事迹怎么写
2015/11/04 职场文书
小学班主任研修日志
2015/11/13 职场文书
一文读懂navicat for mysql基础知识
2021/05/31 MySQL
opencv读取视频并保存图像的方法
2021/06/04 Python
使用compose函数优化代码提高可读性及扩展性
2022/06/16 Javascript