rollup 实践系列 之 重学 rollup

3,326 阅读6分钟

Lynne,一个能哭爱笑永远少女心的前端开发工程师。身处互联网浪潮之中,热爱生活与技术。

之所以定这个系列目标是因为:

  1. 对构建工具感兴趣,最近开始从 rollup 入手研究打包工具原理;

  2. 有个【工具库项目】在做技术选型时定了 rollup 作为打包工具。

总归是要从 0 到 1 一点一点来,正好借此机会开了这个系列一方面督促自己记录心得所学,一方面也希望与大家有交流讨论,基于作者能力有限,希望阅读过的朋友不吝赐教,褒贬皆收~~~

一、初识 rollup

1. 什么是 rollup

选择一个工具,必定是看重它的某个能力,之于我们的开发而言,rollup 是如何脱颖而出的呢?

先不卖关子,就是看准它轻量、快速、体积小,其中不可忽视的就是 tree-shaking!对于 tree-shaking 的详细分析可以看这篇文章 - 无用代码去哪了?项目减重之 rollup 的 Tree-shaking

但还是多说几句废话,再介绍下真正的我眼中的 rollup ~~~

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

这是所有打包工具必备能力,那么rollup有什么不同之处和优势呢?又或者熟悉构建工具的同学会说:rollup 已经老生常谈,知道就行了!

但是,从 Webpack2.x 通过插件逐步实现风起于 rollup 的 tree-shaking 功能,到最近炙手可热的 Vite 构建工具也借助了 rollup 的打包能力,众所周知 Vue 和 React 也是使用 rollup 进行打包的,尤其当我们创建函数库、工具库等库的打包时,首选也是 rollup!因此,掌握 rollup 还是非常有必要的!

  • rollup 使用 ES6 标准格式打包代码
  • 只打包 js ,打包速度快,打包生成的包体积小
  • 在处理纯代码上具有算法优势,适用于开发 js 库,当然打包应用开发也可使用

什么时候 rollup 是不推荐使用的呢?

  • 需要代码拆分(Code Splitting),rollup 不支持 Code Splitting
  • 很多静态资源需要处理,复杂模块化
  • 构建的项目需要引入很多 CommonJS 模块的依赖

这时候推荐 Webpack 或者 Vite, 最近 Vite 真是风头正盛,rollup 之后会研究下Vite。

roullup 做了什么?

将消除无用代码后的诸多小块代码编译成为大块复杂可运行在浏览器上的代码。

更详细地来讲就是:

  1. rollup 的打包功能意味着 - 我们可以在开发阶段将项目单独拆分成小的模块,以便独立开发,既能减少意料之外的交互,又能降低解决问题的复杂度。

  2. 静态分析代码中的 import,并删除任何未实际使用的代码,即Tree-shaking。这允许我们在使用依赖模块功能的同时,不会增加额外的依赖从而使项目体积显得臃肿。

借用官网的表述:

因为 Rollup 只引入最基本最精简代码,所以可以生成轻量、快速,以及低复杂度的 library 和应用程序。因为这种基于显式的 import 和 export 语句的方式,它远比「在编译后的输出代码中,简单地运行自动 minifier 检测未使用的变量」更有效。

另外在兼容性方面,rollup 打包输出也做了很好的支持。前端同学可以自行了解 JavaScript 模块 (该链接仅做科普)。

在 rollup 中,可将文件编译为 UMD 或 CommonJS 格式,然后在 package.json 文件的 main 属性中指向当前编译的版本。如果 package.json 也具有 module 字段,像 rollup 和 webpack 2(及以上) 这样的 ES6 感知工具将会直接导入 ES6 模块版本。

二、怎么使用 rollup 打包?

有三种方式,由于第一种不适合日常开发项目,所以其实主要是两种,一种是通过可选配置文件和命令行操作,另一种是通过 rollup 的 JS API。

简单介绍下这三种方式。

3.1 命令行

采用纯命令行打包方式,假设应用程序或 JS 库的入口文件为 main.js,且所有的导入都被编译到一个名为bundle.js的文件中。

以下打包命令行仅 format 不同,目的是使生成的打包文件可以运行在不同环境

浏览器 - rollup main.js --format iife --name "myBundle" --file bundle.js

Node.js - rollup main.js --format cjs --file bundle.js

浏览器 & Node.js - rollup main.js --format umd --file bundle.js

3.2 配置文件 + 命令行

当我们使用命令行打包方式涉及引入更多打包参数时,这种方式就会变得复杂。一方面难以记忆,另一方面每次都一长串代码很容易出错。

同样地,入口文件为 main.js,且所有的导入都被编译到一个名为bundle.js的文件中。我们可以借助配置文件以更简洁的代码实现打包。

关键点就在于 rollup 的配置文件

默认 rollup.config.js,配置文件是一个 ES6 模块,它对外暴露一个对象,这个对象包含了一些Rollup需要的一些选项。

举个栗子:

// rollup.config.js
import json from 'rollup-plugin-json';

export default {
  input: 'src/main.js', // 打包入口文件
  output: {
    file: 'bundle.js',  // 打包输出文件
    format: 'cjs' // es/umd 也可,相当于配置命令行打包方式中的format,需要了解 JS 模块化知识
  },
  plugins: [ json() ] // 丰富的插件,自行配置
};

然后配合 package.json 完成打包:

{
  "devDependencies": {
    "rollup": "^2.47.0"
  },
  "scripts": {
    "build": "rollup -c"
  }
}

3.3 JavaScript API

rollup.rollup()

返回一个 Promise,它解析了一个 bundle 对象,此对象带有不同的属性及方法,比如 bundle.generate() 和 bundle.write()。

rollup.watch()

当检测到磁盘上单个模块改变,会重新构建你的 bundle,当通过命令行运行 Rollup,并带上 --watch 标记时,此函数会被内部使用。

不详细介绍,原因有二:

  • 很少使用...
  • 学 API 没什么意思,用到时候再来查

实际使用场景我只见到过一个,参考 rollup 源码中的 debug 文件内容:

// perf-debug.js
loadConfig().then(async config => // 获取收集配置
    (await rollup.rollup(config)).generate( 
        Array.isArray(config.output) ? config.output[0] : config.output
    )
);

三、rollup 打包原理怎么实现

这是下一篇要讲的,先把 实现打包的 demo 贴上 -> rollup-demo

四、系列规划

rollup 系列主要部分

该系列不定期更新,因为会穿插项目需要开发心得~~~