一文带你快速上手 Rollup

以下文章来源于前端森林,作者 前端森林(微信公众号)
如有侵权,请联系作者删除!

前言

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

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

什么是 rollup ?

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

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

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

webpack 偏向于应用打包的定位不同,rollup.js 更专注于 JavaScript 类库打包。

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

为什么是 Rollup ?

webpack 我相信做前端的同学大家都用过,那么为什么有些场景还要使用 rollup 呢?这里我简单对 webpackrollup 做一个比较:

总体来说 webpackrollup 在不同场景下,都能发挥自身优势作用。 webpack 对于代码分割和静态资源导入有着“先天优势”,并且支持热模块替换( HDR ),而 rollup 并不支持。

所以当开发应用事可以优先选择 webpack ,但是 rollup 对于代码的 Tree-shaking 和 ES6 模块有着算法优势上的支持,若你项目只需要打包出一个简单的 bundle 包,并是基于 ES6 模块开发的,可以考虑使用 rollup

扫描二维码关注公众号,回复: 12510792 查看本文章

其实 webpack2.0 开始就已经支持 Tree-shaking ,并在使用 babel-loader 的情况下还可以支持 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’也可以)、lifeumd 中的任何一个。
  • -o-o 指定了输出的路径,这里我们将打包后的文件输出到 dist 目录下的 bundle.js
  • -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 表示输出文件的内容,它允许传入一个对象或一个数组,当为数组时,一次输出多个文件,它包含一下内容:

    1.output.file : 输出文件的路径(老版本为 dest,已经废弃)
    2.output.format :输出文件的格式
    3.output.banner :文件头部添加的内容
    4.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 (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-common.js

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.js 目录

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

猜你喜欢

转载自blog.csdn.net/lb1135909273/article/details/110473257