rollup输出的6种格式详解

目录
  • 学习本文
  • 为什么要学这个?
  • DEMO与示例构建
  • 一、IIFE 自执行函数
    • 1.1 打包结果分析
    • 1.2 如何运行
    • 1.3 优缺点
  • 二、CommonJS
    • 2.1 分析打包结果
    • 2.2 如何运行
    • 2.3 优缺点
  • 三、AMD 和 requirejs !
    • 3.1 打包结果分析
    • 3.2 如何运行
    • 3.3 优缺点
  • 四、UMD 伟大的整合
    • 4.1 打包分析
    • 4.2 如何运行?
    • 4.3 优缺点
  • 五、SystemJs
  • 六、ESM
    • 6.1 打包分析
    • 6.2 如何运行
  • 总结:分别适合在什么场景使用?

学习本文

之前找工作的时候因为在简历上写“熟练使用 rollup.js 进行构建”,但回答不出 rollup 能打包出哪几种格式,我差点被面试官鄙视。你将:

  • 弄明白vite和rollup输出的几种格式,了解它们之间的关系和使用场景。
  • 获得一份最简单的 rollup.js 打包 demo 一份。
  • 这份 demo 还为每个打包结果准备了一个可执行 demo of demo

为什么要学这个?

伴随着 vite.js 越来越火,它也变成了工作生产和面试问答时被经常提及的问题。

众所周知,vite.js 构建的核心是 rollup.js

那为什么在学习它们之前,有必要先学习 js 模块化规范?

因为我们需要理解它们最终的产物是什么,是如何运作的。

“崇拜魔法”是编程学习者的大忌,“好奇心”才是我们成长与进步的阶梯。

让我们解开神秘的面纱,看看那些打包后的代码,都是些“什么玩意儿”!

DEMO与示例构建

请配合 demo 食用,你会收获更多!

我为本文构建了一个简单的 demo 实例,源码就放在 github 上。

demo 地址: github.com/zhangshichu…demo 的代码结构如下:

├── answer.js
├── index.js
├── out
├── package.json
├── rollup.config.js
└── yarn.lock

其中 index.jsanswer.js 属于业务代码,是需要被打包的对象。

index.js 中依赖了 answer.js

如下:

// answer.js
export default 42

// index.js
import answer from "./answer";
import { repeat } from 'lodash'
// 定义一个无用变量, 测试 tree-shaking
const unusedVar = 'May the 4th'

export const printAnswer = () => {
  // 打印
  console.log(`the answer is ${answer}`)
  // 测试 lodash 的能力,打印42个1
  console.log(repeat('1', answer))
}

rollup.config.jsrollup 的配置文件,在此文件中我们分别指定其输出 amd , cjs , esm , iife , umd , system 六种格式的文件。

输出的文件会被打包到 out 文件夹下。

当我们执行 yarn build 或者 npm build 之后,会在 out 下产生如下格式的代码:

├── out
│   ├── amd
│   │   └── bundle.js
│   ├── cjs
│   │   └── bundle.js
│   ├── ems
│   │   └── bundle.js
│   ├── iife
│   │   └── bundle.js
│   ├── system
│   │   └── bundle.js
│   └── umd
│       └── bundle.js

为了弄清楚每种格式的 bundle.js 文件是如何运作的,我专门为它们订制添加了一些附属的小 demo

接下来,就让我们一种格式一种格式地学习和分析吧。

一、IIFE 自执行函数

IIFE 的全称是 “immediately invoked function expression”。

1.1 打包结果分析

让我们先看看本 demoiife 格式打出来的包长什么样。

对上述代码做一些简化:

var Test = (function (exports, lodash) {
  'use strict'; // 自带严格模式,避免一些奇怪的兼容性问题

  /**
   * 下面折行无用代码被 tree-shaking 掉了
   * const unusedVar = 'May the 4th'
   * */

  var answer = 42; // 业务中被单一引用的模块,被直接抹平了

  const printAnswer = () => {
    console.log(`the answer is ${answer}`);

    console.log(lodash.repeat('1', answer));
  };

  exports.printAnswer = printAnswer; // 把要export的属性挂在到exports上

  return exports;

})({}, $); // exports是第一个入参,依赖的jquery是第二个入参

IIFE 是前端模块化早期的产物,它的核心思路是:

  • 构建一个匿名函数
  • 立刻执行这个匿名函数,对外部的依赖通过入参的形式传入
  • 返回该模块的输出

1.2 如何运行

IIFE 的运行其实很容易,如果它没有其他依赖,只需要去引入文件,然后在 window 上取相应的变量即可。
如:

  <script src="http://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script>
    // jquery 就是典型的自执行函数模式,当你引入后,他就会挂在到 window.$ 上
    window.$ // 这样就能取到 jquery 了
  </script>

但是如果你像本 demo 中那样依赖了其他的模块,那你就必须保证以下两点才能正常运行:

  • 此包所依赖的包,已在此包之前完成加载。
  • 前置依赖的包,和 IIFE 只执行入参的变量命名是一致的。

以本 demoIIFE 构建结果为例:

  • 它前置依赖了 lodash,因此需要在它加载之前完成 lodash 的加载。
  • IIFE 的第二个入参是 lodash,作为前置条件,我们需要让 window.lodash 也指向 lodash

因此,运行时,代码如下:

<head>
  <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
  <script>window.lodash = window._</script>
  <script src="./bundle.js"></script>
</head>
<body>
  <script>
    window.Test.printAnswer();
  </script>
</body>

1.3 优缺点

优点:

  • 通过闭包营造了一个“私有”命名空间,防止影响全局,并防止被从外部修改私有变量。
  • 简单易懂
  • 对代码体积的影响不大

缺点:

  • 输出的变量可能影响全局变量;引入依赖包时依赖全局变量。
  • 需要使用者自行维护 script 标签的加载顺序。

优点就不细说了,缺点详细解释一下。

缺点一:输出的变量可能影响全局变量;引入依赖包时依赖全局变量。

前半句:输出的变量可能影响全局变量; 其实很好理解,以上面 demo 的输出为例: window.Test 就已经被影响了。
这种明显的副作用在程序中其实是有隐患的。

后半句:引入依赖包时依赖全局变量; 我们为了让 demo 正常运行,因此加了一行代码让 window.lodash 也指向 lodash,但它确实是太脆弱了。

<!-- 没有这一行,demo就无法正常运行 -->
<script>window.lodash = window._</script>

你瞧,IIFE 的执行对环境的依赖是苛刻的,除非它完全不依赖外部包。(Jquery: 正是在下!)

虽然 IIFE 的缺点很多,但并不妨碍它在 Jquery 时代极大地推动了 web 开发的进程,因为它确实解决了 js 本身存在的很多问题。

那么?后续是否还有 更为优秀 的前端模块化方案问世呢?

当然有,往下看吧。

二、CommonJS

2.1 分析打包结果

先看看 CommonJs 打包的结果:

简化一下,就长这样了:

'use strict';

var lodash = require('lodash');

var answer = 42;

const printAnswer = () => {
  // 打印
  console.log(`the answer is ${answer}`);
  // 测试 lodash 的能力,打印42个1
  console.log(lodash.repeat('1', answer));
};

exports.printAnswer = printAnswer;

以上格式,就是 CommonJS 规范的写法。

// CommonJS 通过一个全局 require 方法进行模块的引入
var lodash = require('lodash');
// CommonJS 进行模块内方法输出的方式
module.exports.printAnswer = printAnswer;
// 上面写法也等价于:
exports.printAnswer = printAnswer;
// 因为 exports 变量等价于 module.exports

为了解决 node.js 在模块化上的缺失, 2009年10月 CommonJS 规范首次被提出。

注意这个关键词: node.js

是的,CommonJS 并不是在浏览器环境运行的规范,而是在 node.js 环境下运行的。

2.2 如何运行

因此,我写了一个 run.js 脚本。 如下:

// run.js
const Test = require('./bundle.js')
Test.printAnswer()

然后,执行以下命令:

# 执行脚本
node ./out/cjs/run.js
# 输出1:
> the answer is 42
# 输出2:
> 111111111111111111111111111111111111111111

可以看出,node.js 环境是天然支持 CommonJS 的。

2.3 优缺点

优点

完善的模块化方案,完美解决了 IIFE 的各种缺点。

缺点

不支持浏览器环境,因为这种同步的引入方式可能导致浏览器假死。

因此,前端界迫切地需要一种能在浏览器环境完美运行,完善的模块化方案。

三、AMD 和 requirejs !

AMD,YES!

2011年, amdjs-api 在业内被正式提出。

3.1 打包结果分析

amd 格式的打包结果如下:

可以看到,核心内容是一个全局方法 define

define 方法有三个入参,分别是:

  • "Test", 模块名称
  • [exports, lodash] 分别表示模块的输出和外部依赖
  • 一个以 exportslodash 作为入参的方法,代表模块的实际内容。

相比于 IIFECommonJs 而言,AMD 的写法无疑是复杂且别扭的。

但它却实实在在是解决了 IIFECommonJS 所面临的问题,对“浏览器里完善的JS模块方法” 提供了一套完善的方案。

尤其是 amd 标准的实现方案:requirejs

requirejs 所实现的 AMD 不仅解决了 CommonJS 在浏览器端的不适,通过异步的方式进行模块加载实现了不会导致假死的能力;更是完全弥补了 IIFE 存在的各类缺陷。

requirejs 在使用时,一般情况下是以下四步法:

  • 在浏览器内引入 require.js
  • 通过 requirejs.config 方法定义全局的依赖
  • 通过 requirejs.define 注册模块
  • 通过 requirejs() 完成模块引入。

3.2 如何运行

out/amd 打包目录下的 index.html 里,按如下方式编排代码:

<head>
  <!-- 1. 引入 require.js -->
  <script src="./require.js"></script>
  <!-- 2. 定义全局依赖 -->
  <script>
    window.requirejs.config({
      paths: {
        "lodash": "https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min"
      }
    });
  </script>
  <!-- 3. 定义模块 -->
  <script src="./bundle.js"></script>
</head>
<body>
  <script>
    // 4. 开销模块
    window.requirejs(
      ['Test'],
      function   (test) {
        test.printAnswer()
      }
    );
  </script>
</body>

打开浏览器,我们可以正常地看到]控制台里被打印出来的 4242个1 了。

3.3 优缺点

优点

  • 解决了 CommonJS 的缺点
  • 解决了 IIFE 的缺点
  • 一套完备的浏览器里 js 文件模块化方案

缺点

  • 代码组织形式别扭,可读性差

但好在我们拥有了各类打包工具,浏览器内的代码可读性再差也并不影响我们写出可读性ok的代码。

现在,我们拥有了面向 node.jsCommonJs面向浏览器的 AMD 两套标准。

如果我希望我写出的代码能同时被浏览器nodejs识别,我应该怎么做呢?

四、UMD 伟大的整合

它没有做什么突破性的创造,但它是集大成者。

4.1 打包分析

umd 格式构建出来的代码的可读性进一步降低了。

我相信任何正常人看到下面这段代码都会感到一阵头大:

是的,整整一大段代码,只是在处理兼容性问题,判断当前应该使用 amd 亦或是 CommonJS

因此 umd 的代码和实现不在此进行过多分析,它所做的无非便是让同一段代码兼容了 amdCommonJS

4.2 如何运行?

  • 在浏览器端,它的运行方式和 amd 完全一致,可以完全参考 3.2 节的 demo
  • 在node.js端,它则和 CommonJS 的运行方式完全一致,在此就不赘述了。

4.3 优缺点

优点

抹平了一个包在 AMDCommonJS 里的差异

缺点

会为了兼容产生大量不好理解的代码。(理解难度与包体积)

虽然在社区的不断努力下,CommonJSAMDUMD 都给业界交出了自己的答卷。

但很显然,它们都是不得已的选择。

浏览器应该有自己的加载标准。

ES6 草案里,虽然描述了模块应该如何被加载,但它没有 “加载程序的规范”。

五、SystemJs

因此 WHATWG(Web Hypertext Application Technology Working Group) 即网页超文本应用技术工作小组,提出了一套更有远见的规范:whatwg/loader

也就是 JavaScript Loader Standard (JS 加载标准)。

本规范描述了从 JavaScript 宿主环境中加载 JavaScript 模块的行为。它还提供了用于拦截模块加载过程和自定义加载行为的 api。

基于此规范,SystemJS 诞生了。

SystemJS 是目前 whatwg/loader 规范的最佳实践者。

可以看出来,system 的打包结果其实和 amd 类似,提供了全局的对象 System,并提供了注册的方式和统一的写法。

就单纯的从打包结果上,其实看不出它相比对 AMD + require.js 有什么优势,难道只是写法上存在差异?

并不止于此!

相比于 require.jsSystemJSSystem.import('module/name') 方式允许你更为“懒”地加载模块,这意味着你无需每次都加载一大堆的 bundle,用户只需要为他能看见的页面开销带宽。

另外,正因为 SystemJS 是面向 whatwg/loader 规范实践的,因此它是面向未来的模块依赖方式。

抱歉,这个的 demo 我也没玩明白,就不误导大家了。希望有明白的大佬可以帮忙完善下demo。

六、ESM

ECMAScript modules, 也叫 ESM, Javascript 模块化官方标准格式。

6.1 打包分析

ESM 被提出来之前,JavaScript 一直没有真正意义上的模块(module)体系。

它的规范是通过 export 命令显式指定输出的代码,再通过 import 命令输入。

// 导入模块
import { foo } from 'bar';
// 导出命令
export { zap };

这也是我们日常开发中最为熟悉的写法。

因此,esm 格式打出来的包,可读性确实非常棒:

和阅读我们平时所写的业务代码完全没有区别。(rollup 依然没忘记做 tree-shaking

6.2 如何运行

祝贺你,是这个时代的前端开发。

部分现代浏览器已经开始实装 <script type="module> 了,因此在浏览器上直接使用 esm 已成为现实。

但运行起来扔需要做一些前置步骤。

  • 在js-modules目录下起一个本地静态服务
# 在js-modules目录下起一个本地静态服务
cd js-modules && http-server
  • esm/bundle.js 文件的第一行修改为:
import repeat from '../../node_modules/lodash-es/repeat.js';
// 因为默认的lodash并不是输出的 esm 格式,因此为了demo我们需要做一些特殊处理
  • 在浏览器打开页面(假设端口是8080),则打开:http://127.0.0.1:8080/out/esm/index.html

这样一来,代码就能成功运行,控制台就可以成功打印 42 和 42个1 了。

总结:分别适合在什么场景使用?

  • IIFE: 适合部分场景作为SDK进行使用,尤其是需要把自己挂到 window 上的场景。
  • CommonJS: 仅node.js使用的库。
  • AMD: 只需要在浏览器端使用的场景。
  • UMD: 既可能在浏览器端也可能在node.js里使用的场景。
  • SystemJs: 和UMD类似。目前较出名的 Angular 用的就是它。
  • ESM: 1. 还会被引用、二次编译的场景(如组件库等);2.浏览器调试场景如 vite.js的开发时。3.对浏览器兼容性非常宽松的场景。

以上就是rollup输出的6种格式详解的详细内容,更多关于rollup输出6种格式的资料请关注我们其它相关文章!

(0)

相关推荐

  • 基于rollup的组件库打包体积优化小结

    背景 前段时间对公司内部的组件库(类似element-ui)做了打包体积优化,现在抽点时间记录下.以前也做过构建速度的优化,具体可以看组件库的webpack构建速度优化 一些存在的问题 最开始打包是基于webpack的,在按需加载上存在的体积冗余会比较大,如: webpack打包特有的模块加载器函数,这部分其实有些多余,最好去掉 使用babel转码时,babel带来的helper函数全部是内联状态,需要转成import或require来引入 使用transform-rumtime对一些新特性添加

  • Rollup处理并打包JS文件项目实例代码

    关于Rollup rollup是一款用来es6模块打包代码的构建工具(支持css和js打包).当我们使用ES6模块编写应用或者库时,它可以打包成一个单独文件提供浏览器和Node.js来使用. 它的优点有如下: 能组合我们的脚本文件. 移除未使用的代码(仅仅使用ES6语法中). 在浏览器中支持使用 Node modules. 压缩文件代码使文件大小尽可能最小化. Rollup最主要的优点是它是基于ES2015模块的,相比于webpack或Browserify所使用的CommonJS模块更加有效率,

  • 使用webpack和rollup打包组件库的方法

    前言 之前做了一个loading的样式组件,为了实现代码的可重用性,将这个小项目打包并且发布在了npm上.在一次次的打包发包过程中经历了一个有一个报错,@buzuosheng/loading这个组件已经到了2.7.0版本,虽然还有一些要调整的地方,但总算是可以用了. webpack和rollup对比 webpack算是使用程序员使用最多的打包工具,面试中往往会问到webpack的相关问题,而rollup被问到的要少很多.导致这种现象的一个原因是,应用开发使用webpack,库开发使用rollup

  • Oracle分组函数之ROLLUP的基本用法

    rollup函数 本博客简单介绍一下oracle分组函数之rollup的用法,rollup函数常用于分组统计,也是属于oracle分析函数的一种 环境准备 create table dept as select * from scott.dept; create table emp as select * from scott.emp; 业务场景:求各部门的工资总和及其所有部门的工资总和 这里可以用union来做,先按部门统计工资之和,然后在统计全部部门的工资之和 select a.dname,

  • 使用rollup打包JS的方法步骤

    rollup 采用 es6 原生的模块机制进行模块的打包构建,rollup 更着眼于未来,对 commonjs 模块机制不提供内置的支持,是一款更轻量的打包工具.rollup 比较适合打包 js 的 sdk 或者封装的框架等,例如,vue 源码就是 rollup 打包的.而 webpack 比较适合打包一些应用,例如 SPA 或者同构项目等等. 创建项目 目录结构是这样的: hey-rollup/ ├── dist │ ├── bundle-name.js │ └── bundle-name.m

  • 一篇文章带你从零快速上手Rollup

    前言 项目中一直用的都是webpack,前一段需要开发几个类库供其他平台使用,本来打算继续用webpack的,但感觉webpack用来开发js库,不仅繁琐而且打包后的文件体积也比较大.正好之前看vue源码,知道vue也是通过rollup打包的.这次又是开发类库的,于是就快速上手了rollup. 本篇文章是我有了一定的项目实践后,回过来给大家分享一下如何从零快速上手rollup. 什么是rollup? 系统的了解rollup之前,我们先来简单了解下What is rollup? 关于rollup的

  • rollup输出的6种格式详解

    目录 学习本文 为什么要学这个? DEMO与示例构建 一.IIFE 自执行函数 1.1 打包结果分析 1.2 如何运行 1.3 优缺点 二.CommonJS 2.1 分析打包结果 2.2 如何运行 2.3 优缺点 三.AMD 和 requirejs ! 3.1 打包结果分析 3.2 如何运行 3.3 优缺点 四.UMD 伟大的整合 4.1 打包分析 4.2 如何运行? 4.3 优缺点 五.SystemJs 六.ESM 6.1 打包分析 6.2 如何运行 总结:分别适合在什么场景使用? 学习本文

  • php数组(array)输出的三种形式详解

    复制代码 代码如下: $bbbb=array("11"=>"aaa","22"=>"bbb");//只能输出值value不能输出keyforeach($bbbb as $color){ echo $color;}//value与key都可输出foreach($bbbb as $key=>$value){ echo $key."=>".$value;} //value与key都可输出w

  • python的格式化输出(format,%)实例详解

    皇城PK Python中格式化字符串目前有两种阵营:%和format,我们应该选择哪种呢? 自从Python2.6引入了format这个格式化字符串的方法之后,我认为%还是format这根本就不算个问题.不信你往下看. # 定义一个坐标值 c = (250, 250) # 使用%来格式化 s1 = "敌人坐标:%s" % c 上面的代码很明显会抛出一个如下的TypeError: TypeError: not all arguments converted during string f

  • json文件书写格式详解

    目录  JSON是什么 为什么有这个技术 JSON 如何使用 - 数据格式 - 注意事项 - JS 内置两个Json方法 实例:  JSON是什么 JSON ( JavaScript Object Notation) ,是一种数据交互格式. 为什么有这个技术 Json之前,大家都用 XML 传递数据.XML 是一种纯文本格式,所以适合在网络上交换数据,但是 XML 格式比较复杂,终于道格拉斯·克罗克福特(Douglas Crockford)发明了JSON 这种超轻量级的数据交换格式. JSON

  • Java实现Excel转PDF的两种方法详解

    目录 一.使用spire转化PDF 1.使用spire将整个Excel文件转为PDF 2.指定单个的sheet页转为PDF 二.使用jacob实现Excel转PDF(推荐使用) 1.环境准备 2.执行导出PDF 使用具将Excel转为PDF的方法有很多,在这里我给大家介绍两种常用的方法,分别应对两种不一样的使用场景,接下来我在springboot环境下给大家做一下演示! 一.使用spire转化PDF 首先介绍一种比较简单的方法,这种方法可以使用短短的几行代码就可以将我们的Excel文件中的某一个

  • Python比较两个日期的两种方法详解

    目录 datetime strptime 之前我们曾经分享过:Python获取某一日期是“星期几”的6种方法!实际上,在我们使用Python处理日期/时间的时候,经常会遇到各种各样的问题.今天我们就来探讨另一个问题,如何用Python比较两个日期? datetime 如果需要用Python处理日期和时间,大家肯定会先想到datetime.time.calendar等模块.在这其中,datetime模块主要是用来表示日期时间的,就是我们常说的年月日/时分秒. datetime模块中常用的类: 类名

  • Springboot配置返回日期格式化五种方法详解

    目录 格式化全局时间字段 1.前端时间格式化(不做无情人) 2.SimpleDateFormat格式化(不推荐) 3.DateTimeFormatter格式化(不推荐) 4.全局时间格式化(推荐) 实现原理分析 5.部分时间格式化(推荐) 总结 应急就这样 格式化全局时间字段 在yml中添加如下配置: spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 或者 spring: jackson: ## 格式为yyyy-MM-dd HH:mm:ss date-

  • 基于ScheduledExecutorService的两种方法(详解)

    开发中,往往遇到另起线程执行其他代码的情况,用java定时任务接口ScheduledExecutorService来实现. ScheduledExecutorService是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响. 注意,只有当调度任务来的时候,ScheduledExecutorService才会真正启动一个线程,其余时间ScheduledExecutorService都是处于轮询任务的状态. 1.scheduleAtFix

  • 使用Java构造和解析Json数据的两种方法(详解二)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Json数据的方法

  • 使用Java构造和解析Json数据的两种方法(详解一)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Json数据的方法

随机推荐