一篇文章带你从零快速上手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 相关文章推荐
jQuery的一些注意
Dec 06 Javascript
Extjs学习笔记之三 extjs form更多的表单项
Jan 07 Javascript
Jquery 的扩展方法总结
Oct 01 Javascript
JavaScript函数定义的常见注意事项小结
Sep 16 Javascript
Javascript毫秒数用法实例
Feb 05 Javascript
实现JavaScript高性能的数据存储
Dec 11 Javascript
你应该了解的JavaScript Array.map()五种用途小结
Nov 14 Javascript
vue中过滤器filter的讲解
Jan 21 Javascript
详解vue组件中使用路由方法
Feb 12 Javascript
浅谈js闭包理解
Apr 01 Javascript
jQuery实现动态生成年月日级联下拉列表示例
May 11 jQuery
解决使用layui对select append元素无效或者未及时更新的问题
Sep 18 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中三个等号(===)和两个等号(==)的区别
2013/08/06 PHP
CI操作cookie的方法分析(基于helper类库)
2016/03/28 PHP
php实用代码片段整理
2016/11/12 PHP
php版微信小店API二次开发及使用示例
2016/11/12 PHP
php正则判断是否为合法身份证号的方法
2017/03/16 PHP
PHP递归实现汉诺塔问题的方法示例
2017/11/25 PHP
PHP二维数组分页2种实现方法解析
2020/07/09 PHP
javascript 硬盘序列号+其它硬件信息
2008/12/23 Javascript
Javascript面向对象扩展库代码分享
2012/03/27 Javascript
使用jquery prev()方法找到同级的前一个元素
2014/07/11 Javascript
JavaScript使用循环和分割来替换和删除元素实例
2014/10/13 Javascript
jquery分析文本里url或邮件地址为真实链接的方法
2015/06/20 Javascript
详解WordPress开发中get_current_screen()函数的使用
2016/01/11 Javascript
Javascript6中字符串的四个新用法分享
2016/09/11 Javascript
JavaScript实现的select点菜功能示例
2017/01/16 Javascript
微信小程序获取手机网络状态的方法【附源码下载】
2017/12/08 Javascript
原生JS实现简单的无缝自动轮播效果
2018/09/26 Javascript
详解用JS添加和删除class类名
2019/03/25 Javascript
d3.js实现图形拖拽
2019/12/19 Javascript
[01:07]2015国际邀请赛 中国区预选赛精彩回顾
2015/06/15 DOTA
[49:08]OpTic vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[01:35:53]完美世界DOTA2联赛PWL S3 Magma vs GXR 第二场 12.13
2020/12/17 DOTA
python脚本实现统计日志文件中的ip访问次数代码分享
2014/08/06 Python
利用python获取当前日期前后N天或N月日期的方法示例
2017/07/30 Python
Python实现mysql数据库更新表数据接口的功能
2017/11/19 Python
解决安装pyqt5之后无法打开spyder的问题
2019/12/13 Python
python3获取文件中url内容并下载代码实例
2019/12/27 Python
Python连接HDFS实现文件上传下载及Pandas转换文本文件到CSV操作
2020/06/06 Python
董事长秘书岗位职责
2013/11/29 职场文书
大学生蛋糕店创业计划书
2014/01/13 职场文书
小学生操行评语大全
2014/04/22 职场文书
生物科学专业自荐书
2014/06/20 职场文书
2014小学语文教师个人工作总结
2014/12/03 职场文书
初三语文教学计划
2015/01/22 职场文书
《棉鞋里的阳光》教学反思
2016/02/20 职场文书
浅谈JS和Nodejs中的事件驱动
2021/05/05 NodeJs