一文带你快速上手Rollup

前言

项目中一直用的都是webpack,前一段须要开发几个类库供其余平台使用,原本打算继续用webpack的,但感受webpack用来开发js库,不只繁琐并且打包后的文件体积也比较大。正好以前看vue源码,知道vue也是经过rollup打包的。此次又是开发类库的,因而就快速上手了rolluphtml

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

什么是rollup

系统的了解rollup以前,咱们先来简单了解下What is rollup?vue

关于rollup的介绍,官方文档已经写的很清楚了:node

Rollup 是一个 JavaScript 模块打包器,能够将小块代码编译成大块复杂的代码,例如 library 或应用程序。webpack

Webpack偏向于应用打包的定位不一样,rollup.js更专一于Javascript类库打包。es6

咱们熟知的VueReact等诸多知名框架或类库都是经过rollup.js进行打包的。web

为何是rollup

webpack我相信作前端的同窗你们都用过,那么为何有些场景还要使用rollup呢?这里我简单对webpackrollup作一个比较:面试

整体来讲webpackrollup在不一样场景下,都能发挥自身优点做用。webpack对于代码分割和静态资源导入有着“先天优点”,而且支持热模块替换(HMR),而rollup并不支持。算法

因此当开发应用时能够优先选择webpack,可是rollup对于代码的Tree-shakingES6模块有着算法优点上的支持,若你项目只须要打包出一个简单的bundle包,并是基于ES6模块开发的,能够考虑使用rollupnpm

其实webpack2.0开始就已经支持Tree-shaking,并在使用babel-loader的状况下还能够支持es6 module的打包。实际上,rollup已经在渐渐地失去了当初的优点了。可是它并无被抛弃,反而因其简单的API、使用方式被许多库开发者青睐,如ReactVue等,都是使用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打包了一个最最简单的demo

可能不少同窗看到这里对于上面命令行中的参数不是很明白,我依次说明下:

  • -f-f参数是 --format的缩写,它表示生成代码的格式, amd表示采用 AMD标准, cjsCommonJS标准, esm(或 es)为 ES模块标准。 -f的值能够为 amdcjssystemesm('es’也能够)、 iifeumd中的任何一个。
  • -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下载远程的库。

webpackbrowserify这样的其余捆绑包不一样,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.jsonscripts中:"build": "rollup -c -w"

会获得如下报错:打包后的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,能够发现报错已经没有了:

打开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 (ereturn 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"],
};

而后打包,发现会出现报错:提示咱们缺乏@babel/core,由于@babel/corebabel的核心。咱们来进行安装:

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/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 官网查询

关注我

我是山月,正致力于天天用五分钟可以看完的简短答案回答一个大厂高频面试题。扫码添加个人微信,备注进群,加入高级前端进阶群.

欢迎关注公众号【互联网大厂招聘】,定时推送大厂内推信息及面试题简答,天天学习五分钟,半年进入大厂中


本文分享自微信公众号 - 全栈成长之路(shanyue-road)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索