前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >构建打包工具Rollup.js入门指南

构建打包工具Rollup.js入门指南

作者头像
用户1462769
发布2019-08-12 19:55:51
2.5K0
发布2019-08-12 19:55:51
举报
文章被收录于专栏:全栈者

最近在看Vue源码的时候发现一个新的打包工具Rollup.js,之前没有听说过这个工具,也不了解Rollup.js相比于常用的打包工具webpack有什么异同和优势,随后查了一下了解到Vue,React,D3,Three.js,Moment源码里都有它的身影,Rollup到底什么?这篇文章带你走进Rollup的世界。

因为笔者习惯在学习新东西的时候,先从已经了解掌握类似功能的东西出发,寻找差异点,由浅入深,渐进学习,故此篇先从耳熟能详的webpack开始讲起。

webpack的几大特性

代码拆分

在webpack中,代码分离是最引人注目的特性之一。因为对于前端而言,资源包与依赖文件体积过大,将会直接影响性能。尤其是在移动互联网时代,大文件的加载问题也会使得用户体验直线下降,所以如何拆分代码,按需加载是目前很多应用所面临的问题。

在webpack的实现中,通过代码拆分功能将大的资源和依赖文件进行拆解,从而使得当用到某些资源时,能够就只加载这些文件,而避免加载无用资源,使用合理的话,会极大影响加载时间,提示用户体验。

目前webpack常用的代码分离方法有三种:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 SplitChunksPlugin 去重和分离 chunk。
  • 动态导入:通过模块中的内联函数调用来分离代码。

静态模块打包

在webpack的官网上写道,webpack是一个现代JavaScript 应用程序的静态模块打包器,能够打包所有的资源,脚本,图片,样式表等一系列内容均可打包,这也是很多人选择使用webpack基本诉求。

在webpack实现各类资源打包时,由于webpack 自身只理解 JavaScript,所以对于除了JavaScript之外的资源需要使用 loader 让 webpack 能够去处理。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack的loader 可以将所有类型的文件,都转换为应用程序的依赖图和最终的打包结果可以直接引用的模块。目前在官网提供了很多的loader,当然你有兴趣可以写一个自己的loader,通过自定义加载去使用。

插件机制

在webpack中,插件可以完成更多 loader 不能完成的功能,webpack内部以插件的形式提供了灵活强大的自定义 api 功能,其本身暴露了webpack在运行的整个生命周期钩子函数,从而方便注册插件和使用插件,可见webpack的插件是直接对整个构建过程其作用。

Rollup的特点

JavaScript 模块打包器

Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如一些后台语言的编译功能,将代码压缩成一个 library 或应用程序,Rollup 对代码模块使用新的ES6 版本中的标准化格式,并非是 CommonJS 和 AMD这种自定义的解决方案。

Tree-shaking

Tree-shaking, 也被称为 "live code inclusion" ,指清除在项目中没有实际用到的冗余代码。相比于Webpack和Browserify使用的CommonJS模块机制,Rollup中使用ES6 版本中的Modules标准格式编写模块代码,使得Rollup对代码做静态分析和从模块中删除无用的代码时更加高效,更容易。

Vue的作者尤雨溪在知乎里回答,关于Rollup之所以能高效的用Tree-shaking来消除无用的代码主要为以下四个原因。

  1. import只能作为模块顶层的语句出现,不能出现在 function 里面或是 if 里面。
  2. import 的模块名只能是字符串常量。
  3. 不管 import 的语句出现的位置在哪里,在模块初始化的时候所有的 import 都必须已经导入完成。
  4. import binding 是 immutable 的,类似 const。比如说你不能 import { a } from ‘./a’ 然后给 a 赋值个其他什么东西。
Rollup官方对Rollup.js和webpack怎么看?

Rollup 已被许多主流的 JavaScript 库使用,也可用于构建绝大多数应用程序。但是 Rollup 还不支持一些特定的高级功能,尤其是用在构建一些应用程序的时候,特别是代码拆分和运行时态的动态导入。如果你的项目中更需要这些功能,那使用 Webpack可能更符合你的需求。与Webpack偏向于应用打包的定位不同,rollup.js更专注于Javascript类库打包。

Rollup和Webpack怎么选

先看一下目前使用webpack和rollup的一些应用简单分类。

Webpack

Rollup

vue-cli, create-react-app各类应用脚手架

react,vue,three.js,D3,moment

Rollup作者曾在一篇文章里分析了Webpack和Rollup的不同之处,并且总结到Webpack适合在应用层级Web App上使用,而Rollup更适合使用在独立的JavaScript模块库上。

Use Webpack for apps, and Rollup for libraries

但这不是一个绝对的规则,事实上有许多网站和应用程序使用 Rollup 构建,同样的也有大量的类库使用了 webpack 构建。

对于打包工具的选择,这时候还是要看你具体的需求,如果你的诉求是需要代码拆分,或者你有很多静态资源需要处理,再或者你构建的项目需要引入很多CommonJS模块的依赖,那应该选择 webpack 。但是如果你的代码库是基于 ES6 模块的,而且希望你写的代码能够被其他人直接使用,你更需要的打包工具可能是 Rollup 。

Rollup入门学习

一. 命令行式

1. 全局安装后即可使用。

代码语言:javascript
复制
代码语言:javascript
复制
npm i -g rollup

2. 打包一个文件,例如你要打包当前目录下index.js文件为iife格式的。

代码语言:javascript
复制
rollup index.js -o bundle.js -f iife
代码语言:javascript
复制

ps: 命令行参数

代码语言:javascript
复制
-i, --input                 要打包的文件(必须)
-o, --output.file           输出的文件 (如果没有这个参数,则直接输出到控制台)
-f, --output.format [es]    输出的文件类型 (amd, cjs, es, iife, umd)
-e, --external              将模块ID的逗号分隔列表排除
-g, --globals               以`module ID:Global` 键值对的形式,用逗号分隔开
                              任何定义在这里模块ID定义添加到外部依赖
-n, --name                  生成UMD模块的名字
-m, --sourcemap             生成 sourcemap (`-m inline` for inline map)
--amd.id                    AMD模块的ID,默认是个匿名函数
--amd.define                使用Function来代替`define`
--no-strict                 在生成的包中省略`"use strict";`
--no-conflict               对于UMD模块来说,给全局变量生成一个无冲突的方法
--intro                     在打包好的文件的块的内部(wrapper内部)的最顶部插入一段内容
--outro                     在打包好的文件的块的内部(wrapper内部)的最底部插入一段内容
--banner                    在打包好的文件的块的外部(wrapper外部)的最顶部插入一段内容
--footer                    在打包好的文件的块的外部(wrapper外部)的最底部插入一段内容
--interop                   包含公共的模块(这个选项是默认添加的)

二. 配置文件式

配置文件式打包,可以与webpack的webpack.config.js类比。

1. 新建一个rollup.config.js, 写入以下内容。

代码语言:javascript
复制
// rollup.config.js
export default {
  input: 'index.js',
  output: {
    name: 'cjs',
    file: 'bundle.js',
    format: 'cjs',
  }
};

2. 执行打包

代码语言:javascript
复制
rollup -c //默认走rollup.config.js

配置项属性:

代码语言:javascript
复制
// rollup.config.js
export default {
  // 核心选项
  input,     // 必须
  external,
  plugins,
  // 额外选项
  onwarn,
  // danger zone
  acorn,
  context,
  moduleContext,
  legacy
  output: {  // 必须 (如果要输出多个,可以是一个数组)
    // 核心选项
    file,    // 必须
    format,  // 必须
    name,
    globals,
    // 额外选项
    paths,
    banner,
    footer,
    intro,
    outro,
    sourcemap,
    sourcemapFile,
    interop,
    // 高危选项
    exports,
    amd,
    indent
    strict
  },
};

三. rollup内Api方式

1. 新建一个rollup.js文件,写入以下内容

代码语言:javascript
复制
代码语言:javascript
复制
const rollup = require('rollup');

// see below for details on the options
const inputOptions = {
  input: 'main.js',
};
const outputOptions = {
  file: 'bundle.js',
  format: 'iife'
};

async function build() {
  // create a bundle
  const bundle = await rollup.rollup(inputOptions);
  // generate code and a sourcemap
  await bundle.generate(outputOptions);
  // or write the bundle to disk
  await bundle.write(outputOptions);
}
build();
代码语言:javascript
复制

2. 执行rollup.js文件进行打包

代码语言:javascript
复制
代码语言:javascript
复制
node rollup.js

执行参数列表

代码语言:javascript
复制
// 输入参数
const inputOptions = {
  input, // 唯一必填参数
  external,
  plugins,
  // 高级参数
  onwarn,
  cache,
  // 危险参数
  acorn,
  context,
  moduleContext,
  legacy
};
// 输出参数
const outputOptions = {
  // 核心参数
  file,   // 若有bundle.write,必填
  format, // 必填
  name,
  globals,

  // 高级参数
  paths,
  banner,
  footer,
  intro,
  outro,
  sourcemap,
  sourcemapFile,
  interop,

  // 危险区域
  exports,
  amd,
  indent
  strict
};
代码语言:javascript
复制

Rollup与Webpack对比

1. 安装rollup与webpack

代码语言:javascript
复制
代码语言:javascript
复制
mkdir part1 && cd part1 && mkdir src
npm i --save rollup webpack  webpack-cli
代码语言:javascript
复制

2. 新增打包源文件

代码语言:javascript
复制
// src/main.js
import foo from './foo.js';
export default function () {
  console.log(foo);
}

// src/foo.js
const name = 'hello world!';
const description = 'this is a description';
export default name;

3. 新增rollup.config.js和webpack.config.js

代码语言:javascript
复制
// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    name: 'cjs',
    file: 'rollup/cjs.js',
    format: 'cjs',
  }
};

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'webpack'),
    filename: 'bundle.js'
  },
  mode: 'development'
};

4. 执行打包流程

代码语言:javascript
复制
// package.json
"scripts": {
  "build": "webpack && rollup -c"
}
代码语言:javascript
复制
npm run build

5. 文件结构目录

6. 打包结果

通过rollup打包的文件:rollup/cjs.js

代码语言:javascript
复制
// rollup/cjs.js
'use strict';

const name = 'hello world!';

function main () {
  console.log(name);
}

module.exports = main;
代码语言:javascript
复制

通过webpack打包的文件:webpack/bundle.js

代码语言:javascript
复制
/******/ (function(modules) { // webpackBootstrap
/******/  // The module cache
/******/  var installedModules = {};
/******/
/******/  // The require function
/******/  function __webpack_require__(moduleId) {
/******/
/******/    // Check if module is in cache
/******/    if(installedModules[moduleId]) {
/******/      return installedModules[moduleId].exports;
/******/    }
/******/    // Create a new module (and put it into the cache)
/******/    var module = installedModules[moduleId] = {
/******/      i: moduleId,
/******/      l: false,
/******/      exports: {}
/******/    };
/******/
/******/    // Execute the module function
/******/    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/    // Flag the module as loaded
/******/    module.l = true;
/******/
/******/    // Return the exports of the module
/******/    return module.exports;
/******/  }
/******/
/******/
/******/  // expose the modules object (__webpack_modules__)
/******/  __webpack_require__.m = modules;
/******/
/******/  // expose the module cache
/******/  __webpack_require__.c = installedModules;
/******/
/******/  // define getter function for harmony exports
/******/  __webpack_require__.d = function(exports, name, getter) {
/******/    if(!__webpack_require__.o(exports, name)) {
/******/      Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/    }
/******/  };
/******/
/******/  // define __esModule on exports
/******/  __webpack_require__.r = function(exports) {
/******/    if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/      Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/    }
/******/    Object.defineProperty(exports, '__esModule', { value: true });
/******/  };
/******/
/******/  // create a fake namespace object
/******/  // mode & 1: value is a module id, require it
/******/  // mode & 2: merge all properties of value into the ns
/******/  // mode & 4: return value when already ns object
/******/  // mode & 8|1: behave like require
/******/  __webpack_require__.t = function(value, mode) {
/******/    if(mode & 1) value = __webpack_require__(value);
/******/    if(mode & 8) return value;
/******/    if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/    var ns = Object.create(null);
/******/    __webpack_require__.r(ns);
/******/    Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/    if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/    return ns;
/******/  };
/******/
/******/  // getDefaultExport function for compatibility with non-harmony modules
/******/  __webpack_require__.n = function(module) {
/******/    var getter = module && module.__esModule ?
/******/      function getDefault() { return module['default']; } :
/******/      function getModuleExports() { return module; };
/******/    __webpack_require__.d(getter, 'a', getter);
/******/    return getter;
/******/  };
/******/
/******/  // Object.prototype.hasOwnProperty.call
/******/  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/  // __webpack_public_path__
/******/  __webpack_require__.p = "";
/******/
/******/
/******/  // Load entry module and return exports
/******/  return __webpack_require__(__webpack_require__.s = "./src/main.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./src/foo.js":
/*!********************!*\
  !*** ./src/foo.js ***!
  \********************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nconst name = 'hello world!';\nconst description = 'this is a description';\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (name);\n\n//# sourceURL=webpack:///./src/foo.js?");

/***/ }),

/***/ "./src/main.js":
/*!*********************!*\
  !*** ./src/main.js ***!
  \*********************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _foo_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./foo.js */ \"./src/foo.js\");\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function () {\n  console.log(_foo_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n});\n\n//# sourceURL=webpack:///./src/main.js?");

/***/ })

/******/ });
代码语言:javascript
复制

7. 总结一下

上面通过webpack和Rollup打包了一段简单的代码,打包时都是配置为没有经过压缩的情况,从打包的结果中可以看出以下几点,简单做个对比。

Webpack

值得一提的是,在rollup打包的结果文件中,对于在源文件foo.js下的无用代码。const description = 'this is a description';已经被剔除了,而webpack中依旧存在。总的来说,rollup在打包类库时更加干净利落一些,而webpack则有点像大象踩蚊子,大材小用,笨重一点。当然以上只是打包JavaScript的类库文件打包的情况,对于 应用级的打包构建这里就不做对比了。

文中用到的代码均可在此进行获取: https://github.com/FantasyGao/Practice-book/tree/master/rollup


如上内容均为自己总结,难免会有错误或者认识偏差,如有问题,希望大家留言指正,以免误人,若有什么问题请留言,会尽力回答之。如果对你有帮助不要忘了分享给你的朋友或者点击右下方的“在看”哦!也可以关注作者,查看历史文章并且关注最新动态,助你早日成为一名全栈工程师!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • webpack的几大特性
  • 代码拆分
  • 静态模块打包
  • 插件机制
  • Rollup的特点
  • JavaScript 模块打包器
  • Tree-shaking
    • Rollup官方对Rollup.js和webpack怎么看?
    • Rollup和Webpack怎么选
    • Rollup入门学习
    • 二. 配置文件式
    • Rollup与Webpack对比
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档