rollup输出格式有哪一些,分别是什么
Admin 2022-08-12 群英技术资讯 316 次浏览
之前找工作的时候因为在简历上写“熟练使用 rollup.js
进行构建”,但回答不出 rollup
能打包出哪几种格式,我差点被面试官鄙视。你将:
rollup.js
打包 demo
一份。demo
还为每个打包结果准备了一个可执行 demo of demo
伴随着 vite.js
越来越火,它也变成了工作生产和面试问答时被经常提及的问题。
众所周知,vite.js
构建的核心是 rollup.js
。
那为什么在学习它们之前,有必要先学习 js
模块化规范?
因为我们需要理解它们最终的产物是什么,是如何运作的。
“崇拜魔法”是编程学习者的大忌,“好奇心”才是我们成长与进步的阶梯。
让我们解开神秘的面纱,看看那些打包后的代码,都是些“什么玩意儿”!
请配合 demo
食用,你会收获更多!
我为本文构建了一个简单的 demo
实例,源码就放在 github
上。
demo
地址: github.com/zhangshichu…demo
的代码结构如下:
├── answer.js ├── index.js ├── out ├── package.json ├── rollup.config.js └── yarn.lock
其中 index.js
和 answer.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.js
是 rollup
的配置文件,在此文件中我们分别指定其输出 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 的全称是 “immediately invoked function expression”。
让我们先看看本 demo
的 iife
格式打出来的包长什么样。
对上述代码做一些简化:
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
是前端模块化早期的产物,它的核心思路是:
IIFE
的运行其实很容易,如果它没有其他依赖,只需要去引入文件,然后在 window
上取相应的变量即可。
如:
<script src="http://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script> // jquery 就是典型的自执行函数模式,当你引入后,他就会挂在到 window.$ 上 window.$ // 这样就能取到 jquery 了 </script>
但是如果你像本 demo
中那样依赖了其他的模块,那你就必须保证以下两点才能正常运行:
IIFE
只执行入参的变量命名是一致的。以本 demo
的 IIFE
构建结果为例:
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>
优点:
缺点:
script
标签的加载顺序。优点就不细说了,缺点详细解释一下。
缺点一:输出的变量可能影响全局变量;引入依赖包时依赖全局变量。
前半句:输出的变量可能影响全局变量; 其实很好理解,以上面 demo
的输出为例: window.Test
就已经被影响了。
这种明显的副作用在程序中其实是有隐患的。
后半句:引入依赖包时依赖全局变量; 我们为了让 demo
正常运行,因此加了一行代码让 window.lodash
也指向 lodash
,但它确实是太脆弱了。
<!-- 没有这一行,demo就无法正常运行 --> <script>window.lodash = window._</script>
你瞧,IIFE
的执行对环境的依赖是苛刻的,除非它完全不依赖外部包。(Jquery: 正是在下!)
虽然 IIFE
的缺点很多,但并不妨碍它在 Jquery
时代极大地推动了 web
开发的进程,因为它确实解决了 js
本身存在的很多问题。
那么?后续是否还有 更为优秀 的前端模块化方案问世呢?
当然有,往下看吧。
先看看 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
环境下运行的。
因此,我写了一个 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
的。
优点
完善的模块化方案,完美解决了 IIFE
的各种缺点。
缺点
不支持浏览器环境,因为这种同步的引入方式可能导致浏览器假死。
因此,前端界迫切地需要一种能在浏览器环境完美运行,完善的模块化方案。
AMD,YES!
2011年, amdjs-api
在业内被正式提出。
amd 格式的打包结果如下:
可以看到,核心内容是一个全局方法 define
。
define
方法有三个入参,分别是:
"Test"
, 模块名称[
exports,
lodash]
分别表示模块的输出和外部依赖exports
和 lodash
作为入参的方法,代表模块的实际内容。相比于 IIFE
和 CommonJs
而言,AMD
的写法无疑是复杂且别扭的。
但它却实实在在是解决了 IIFE
和 CommonJS
所面临的问题,对“浏览器里完善的JS模块方法” 提供了一套完善的方案。
尤其是 amd
标准的实现方案:requirejs
。
requirejs
所实现的 AMD
不仅解决了 CommonJS
在浏览器端的不适,通过异步的方式进行模块加载实现了不会导致假死的能力;更是完全弥补了 IIFE
存在的各类缺陷。
requirejs
在使用时,一般情况下是以下四步法:
require.js
requirejs.config
方法定义全局的依赖requirejs.define
注册模块requirejs()
完成模块引入。在 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>
打开浏览器,我们可以正常地看到]控制台里被打印出来的 42
和 42个1
了。
优点
CommonJS
的缺点IIFE
的缺点js
文件模块化方案缺点
但好在我们拥有了各类打包工具,浏览器内的代码可读性再差也并不影响我们写出可读性ok的代码。
现在,我们拥有了面向 node.js
的 CommonJs
和 面向浏览器的 AMD
两套标准。
如果我希望我写出的代码能同时被浏览器和nodejs识别,我应该怎么做呢?
它没有做什么突破性的创造,但它是集大成者。
umd
格式构建出来的代码的可读性进一步降低了。
我相信任何正常人看到下面这段代码都会感到一阵头大:
是的,整整一大段代码,只是在处理兼容性问题,判断当前应该使用 amd
亦或是 CommonJS
。
因此 umd
的代码和实现不在此进行过多分析,它所做的无非便是让同一段代码兼容了 amd
和 CommonJS
。
amd
完全一致,可以完全参考 3.2
节的 demo
。CommonJS
的运行方式完全一致,在此就不赘述了。优点
抹平了一个包在 AMD
和 CommonJS
里的差异
缺点
会为了兼容产生大量不好理解的代码。(理解难度与包体积)
虽然在社区的不断努力下,CommonJS
、 AMD
、 UMD
都给业界交出了自己的答卷。
但很显然,它们都是不得已的选择。
浏览器应该有自己的加载标准。
ES6
草案里,虽然描述了模块应该如何被加载,但它没有 “加载程序的规范”。
因此 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.js
,SystemJS
的 System.import('module/name')
方式允许你更为“懒”地加载模块,这意味着你无需每次都加载一大堆的 bundle
,用户只需要为他能看见的页面开销带宽。
另外,正因为 SystemJS
是面向 whatwg/loader
规范实践的,因此它是面向未来的模块依赖方式。
抱歉,这个的 demo 我也没玩明白,就不误导大家了。希望有明白的大佬可以帮忙完善下demo。
ECMAScript modules, 也叫 ESM, Javascript 模块化官方标准格式。
在 ESM
被提出来之前,JavaScript 一直没有真正意义上的模块(module)体系。
它的规范是通过 export
命令显式指定输出的代码,再通过 import
命令输入。
// 导入模块 import { foo } from 'bar'; // 导出命令 export { zap };
这也是我们日常开发中最为熟悉的写法。
因此,esm
格式打出来的包,可读性确实非常棒:
和阅读我们平时所写的业务代码完全没有区别。(rollup
依然没忘记做 tree-shaking
)
祝贺你,是这个时代的前端开发。
部分现代浏览器已经开始实装 <script type="module>
了,因此在浏览器上直接使用 esm
已成为现实。
但运行起来扔需要做一些前置步骤。
# 在js-modules目录下起一个本地静态服务 cd js-modules && http-server
esm/bundle.js
文件的第一行修改为:import repeat from '../../node_modules/lodash-es/repeat.js'; // 因为默认的lodash并不是输出的 esm 格式,因此为了demo我们需要做一些特殊处理
这样一来,代码就能成功运行,控制台就可以成功打印 42 和 42个1 了。
window
上的场景。Angular
用的就是它。vite.js
的开发时。3.对浏览器兼容性非常宽松的场景。免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了 JavaScript 与 TypeScript之间的联系,JavaScript,也称为 JS,是一种符合 ECMAScript 规范的编程语言。这是一个高级别的、通常是即时编译的、多范式的。TypeScript 是一种强类型、面向对象的编译语言,更多消息内容,需要的朋友可以参考一下下面文章内容
这篇文章主要为大家详细介绍了Vue实现无限加载瀑布流,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
这篇文章主要为大家详细介绍了vue实现列表固定列滚动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
这篇文章我们来了解jQuery删除兄弟节点的相关内容,下文分享的示例是如何删除指定元素后的兄弟节点的方法,文中有很详细的介绍,有需要的朋友可以参考,接下来就跟随小编来一起学习一下吧!
这篇文章主要为大家详细介绍了JavaScript实现搜索的数据显示,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008