一篇文章带你从零快速上手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通用函数
May 09 Javascript
JavaScript可否多线程? 深入理解JavaScript定时机制
May 23 Javascript
JavaScript中的操作符==与===介绍
Dec 31 Javascript
JavaScript三元运算符的多种使用技巧
Apr 16 Javascript
使用console进行性能测试
Apr 27 Javascript
深入分析下javascript中的[]()+!
Jul 07 Javascript
js实现iframe框架取值的方法(兼容IE,firefox,chrome等)
Nov 26 Javascript
利用CSS3在Angular中实现动画
Jan 15 Javascript
jQuery实现图片轮播效果代码(基于jquery.pack.js插件)
Jun 02 Javascript
浅谈Node.js轻量级Web框架Express4.x使用指南
May 03 Javascript
vue-cli项目根据线上环境分别打出测试包和生产包
May 23 Javascript
浅谈vue 二级路由嵌套和二级路由高亮问题
Aug 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
将数字格式的计算结果转为汉字格式
2006/10/09 PHP
php模拟js函数unescape的函数代码
2012/10/20 PHP
基于Swoole实现PHP与websocket聊天室
2016/08/03 PHP
Laravel框架中VerifyCsrfToken报错问题的解决
2017/08/30 PHP
使用prototype.js进行异步操作
2007/02/07 Javascript
JsDom 编程小结
2011/08/09 Javascript
javascript中apply和call方法的作用及区别说明
2014/02/14 Javascript
js脚本获取webform服务器控件的方法
2014/05/16 Javascript
分享jQuery插件的学习笔记
2016/01/14 Javascript
jQuery弹出层后禁用底部滚动条(移动端关闭回到原位置)
2016/08/29 Javascript
js中用cssText设置css样式的简单方法
2016/09/19 Javascript
基于jQuery Easyui实现登陆框界面
2017/07/10 jQuery
JS实现常见的查找、排序、去重算法示例
2018/05/21 Javascript
解决layer弹层遮罩挡住窗体的问题
2018/08/17 Javascript
js的对象与函数详解
2019/01/21 Javascript
简单了解小程序+node梳理登陆流程
2019/06/24 Javascript
javascript实现移动端轮播图
2020/12/09 Javascript
解决await在forEach中不起作用的问题
2021/02/25 Javascript
[00:05]ChinaJoy现场 DOTA2玩家高呼“CN DOTA BEST DOTA”
2019/08/04 DOTA
python入门之语句(if语句、while语句、for语句)
2015/01/19 Python
Django实现表单验证
2018/09/08 Python
python面试题之列表声明实例分析
2019/07/08 Python
利用Python进行图像的加法,图像混合(附代码)
2019/07/14 Python
canvas 阴影和图形变换的示例代码
2018/01/02 HTML / CSS
印度最大的网上花店:Ferns N Petals(鲜花、礼品和蛋糕)
2017/10/16 全球购物
美国最大婚纱连锁店运营商:David’s Bridal
2019/03/12 全球购物
运动鞋、足球鞋和慕尼黑球衣:Sport Münzinger
2019/08/26 全球购物
墨西哥购物网站:Elektra
2020/01/21 全球购物
P D PAOLA法国官网:西班牙著名的珠宝首饰品牌
2020/02/15 全球购物
PHP中如何使用Cookie
2015/10/28 面试题
Linux的文件类型
2016/07/05 面试题
Ajax请求总共有多少种Callback
2016/07/17 面试题
综合办公室个人的自我评价
2013/12/22 职场文书
PHP实现考试倒计时功能代码
2021/04/16 PHP
浅析MongoDB之安全认证
2021/06/26 MongoDB
vscode远程免密登入Linux服务器的配置方法
2022/06/28 Servers