初见 Rollup 的十大常见问题

news2024/10/5 18:39:52

文章目录

  • 初见 Rollup 的十大常见问题
    • 1. 超神奇的 Rollup 英文解释!
    • 2. 为什么 ESM 要比 CommonJS 要好呢?
    • 3. 什么是 tree-shaking ?
    • 4. 如何使用 Rollup 处理 CommonJS?
    • 5. 为什么 node-resolve 不是一个内置功能?
    • 6. 为什么在进行代码分割时,入口块中会出现额外的导入?
    • 7. 如何将 polyfills 添加到 Rollup 包中?
    • 8. Rollup 是用来构建库还是应用程序?
    • 9. Rollup 和 Webpack 有什么区别呢?
    • 10. 如何在浏览器中运行 Rollup?
    • 写在最后

初见 Rollup 的十大常见问题

有看到社区记录了非常多的 Rollup 相关的一些问题,有些也是我刚接触 Rollup 时会疑惑的地方,在这里做一个记录和分享,希望你会喜欢。

在这里插入图片描述

1. 超神奇的 Rollup 英文解释!

如果打开 Rollup 官网,你会发现这样一段英文,好吧,上面图片就有。

rollup.js
The JavaScript module bundler

Compile small pieces of code into something larger and more complex

前面两句好像没什么,Rollup 是一个 JavaScript 的模块打包器,但第二句就神奇了:可以将小块代码编译成大块且复杂的代码

哇!这什么东东,把小的东西整成复杂的?这难道就是传说中真正的高手:简单问题复杂化吗?不知道你是否用同样的疑惑哈,反正我是没太懂。

解释来了:Rollup 是一个 JavaScript 模块打包器,它的主要作用是将多个小的相互依赖的 JavaScript 文件(也就是通常说的 “模块”)合并成一个或多个大的、更复杂的 JavaScript 文件。这些大的文件通常被称为 chunk,可以被浏览器或其他 JavaScript 运行环境直接执行。

这背后的最大原因是:浏览器或其他 JavaScript 运行环境无法解析工程化下的模块

例如,你可能有一个项目,其中包含许多小的 JavaScript 模块文件,每个文件都执行一个特定的任务。使用 Rollup,你可以将这些小的模块文件打包成一个大的 JavaScript 文件,这样在浏览器或 Node.js 环境中就能够执行了。这样做既利用了工程化带来的收益,也让执行环境能正常工作了。

所以,当我们说 Rollup 可以 “将小块代码编译成大块复杂的代码” 时,其意思是它可以将许多小的、相互依赖的模块文件合并成一个大的、包含所有模块代码的文件。

2. 为什么 ESM 要比 CommonJS 要好呢?

好吧,其实这个问题与 Rollup 没有直接的关系,只是常问,也是一个需要了解的点。

ESM 是 JavaScript 工程化结构的官方标准,有着明确发展方向;而 CommonJS 可以说是历史遗留规范,在 ES 模块提出之前用作权宜之计,官方在正式出 ESM 之前,社区几乎都采用的是 CommonJS 规范。

ES 模块允许进行静态分析,这有助于进行诸如 tree-shaking 和作用域提升之类的优化,同时还提供循环引用和实时绑定之类的高级功能。

3. 什么是 tree-shaking ?

Tree-shaking,也被称为"live code inclusion",是 Rollup 用于剔除在项目中未被真正使用代码的一种处理方式。

live code inclusion: 仅包含活代码(有效的代码)

它是一种 死代码消除方式,但就输出大小而言,它比其他方法更有效。名称源自模块的抽象语法树,算法会首先标记所有相关的语句,然后 “摇动语法树” 以删除所有死代码。它的理念类似于标记和清除垃圾收集算法。尽管这个算法并不局限于 ESM,但是 ESM 让 Rollup 可以将所有模块一起视为一个大的并且具有共享绑定的抽象语法树,所以会使得这个算法更加高效。

4. 如何使用 Rollup 处理 CommonJS?

Rollup 致力于实现 ESM 规范,而不在 Node.js、NPM、require() 和 CommonJS 的行为,所以这些模块的加载并不包含在 Rollup 的核心中;但 Rollup 提供了灵活的插件机制,加载 CommonJS 和使用 Node 的模块位置解析逻辑都均可使用插件来实现,只需 npm install commonjs 和 node-resolve 插件,然后通过 rollup.config.js 文件启用它们,就应该可以开始使用了。除此之外,如果模块导入存在 JSON 文件,引入 json 插件即可。

import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [
    nodeResolve(),
    commonjs(),
    json()
  ]
};

5. 为什么 node-resolve 不是一个内置功能?

这主要有两个原因:

  • 从哲学角度来看,这是因为 Rollup 本质上是对 Node 和浏览器中的原生模块加载器的一种 polyfill。在浏览器中,import foo from 'foo' 是无法工作的,因为浏览器并不使用 Node 的解析算法,所以如果 Rollup 直接内置了 node-resolve 的话,这可能导致一些误解。

  • 从实际层面来看,如果一个问题能够通过良好的 API 或者插件进行分离,那么开发软件就会变得容易得多。Rollup 的核心相当大,那么任何任何让它变得更大的设计都需要慎重地权衡。保持 Rollup 的精简,不仅技术债务的可能小更小,修复错误和添加功能也会更容易。

请参阅这个 issue 以获取更详细的解释。

6. 为什么在进行代码分割时,入口块中会出现额外的导入?

默认情况下,当创建多个块时,入口块的依赖项的导入将作为空导入添加到入口块本身。例如:

// input
// main.js
import value from './other-entry.js';
console.log(value);

// other-entry.js
import externalValue from 'external';
export default 2 * externalValue;

// output
// main.js
import 'external'; // this import has been hoisted from other-entry.js
import value from './other-entry.js';
console.log(value);

// other-entry.js
import externalValue from 'external';
var value = 2 * externalValue;
export default value;

实际上,这并不会影响代码执行的顺序或行为,但它会加快你的代码的加载和解析速度。没有这个优化,JavaScript 引擎需要执行以下步骤来运行 main.js

  1. 加载并解析 main.js。在最后,将发现 other-entry.js 的导入。
  2. 加载并解析 other-entry.js。在最后,将发现 external 的导入。
  3. 加载并解析 external
  4. 执行main.js。

有了这个优化,JavaScript 引擎在解析入口模块后将发现所有传递依赖项,避免了瀑布式加载:

  1. 加载并解析 main.js。在最后,将发现导入到 other-entry.jsexternal 的导入。
  2. 加载并解析 other-entry.jsexternal。从 other-entry.js 导入的 external 已经被加载和解析。
  3. 执行main.js。

可能有些情况下你不希望这种优化,这种情况下,你可以通过 output.hoistTransitiveImports 选项关闭它。当使用 output.preserveModules 选项时,也不会应用这种优化。

7. 如何将 polyfills 添加到 Rollup 包中?

尽管Rollup通常会在打包时尽量保持精确的模块执行顺序,但在两种情况下并非总是如此:代码分割和外部依赖。这个问题在外部依赖中最为明显,参见以下示例:

// main.js
import './polyfill.js';
import 'external';
console.log('main');

// polyfill.js
console.log('polyfill');

可以看到,执行顺序是 polyfill.jsexternalmain.js。但如果你将其打包,你会得到:

import 'external';
console.log('polyfill');
console.log('main');

执行顺序会是 externalpolyfill.jsmain.js。哇,怎么回事对吧,这并非是 Rollup 将导入包语句 import 'external' 放在顶部导致的问题,因为无论导入语句位于文件的哪个位置,总是会首先执行的。

这个问题可以通过创建多个 chunk 来解决,比如如果 polyfill.js 最终在与_main.js_ 不同的 chunk 中,那么就会得到正确的执行顺序。然而,Rollup 还没有自动的方式来做到这一点。对于代码分割,情况也是类似,因为 Rollup 始终会试图创建尽可能少的 chunk

对于大多数代码来说,这不是问题,因为Rollup可以保证:如果模块 A 导入模块 B,并且没有循环导入的话,那么 B 总是在 A 之前执行的。

然而,对于 polyfills 来说,这将会是一个问题,因为它们通常需要先执行,但又不希望在每个模块中都放置一个 polyfills 的导入。幸运的是,这并不需要:

  • 如果所有的外部依赖均不依赖 polyfill,那么只需将 polyfill 导入作为每个静态入口点的第一个语句即可。
  • 否则,将 polyfill 作为单独的入口或手动块,就可以确保它始终先执行。

8. Rollup 是用来构建库还是应用程序?

Rollup 已经被许多的 JavaScript 库使用,当然也可以用来构建绝大多数应用程序。然而,如果你想在旧的浏览器上使用代码分割或动态导入,那可能需要一个额外的运行时来处理加载就浏览器缺失的一些 chunk。我们推荐使用 SystemJS Production Build,因为它很好地与 Rollup 格式的输出集成在一起,能够正确处理所有的 ESM 实时绑定和重新导出边缘情况。或者,你也可以使用 AMD 加载器。

9. Rollup 和 Webpack 有什么区别呢?

Rollup 和 Webpack 都是 JavaScript 模块打包器,但它们的关注点和使用场景有所不同:

  1. 设计理念:Rollup 更专注于 JavaScript 模块打包,特别是 ES6 模块,它的设计目标是提供更高效的代码打包和更好的代码优化(如tree-shaking)。而 Webpack 则是一个更通用的模块打包器,它不仅可以处理 JavaScript,还可以处理各种类型的资源,如 CSS、图片、字体等。
  2. 代码分割和动态导入:Webpack 对代码分割和动态导入的支持更为成熟,这使得 Webpack 在构建大型、复杂的前端应用程序时更为强大。而 Rollup 虽然也支持代码分割和动态导入,但可能需要更多的配置和插件。
  3. 输出格式:Rollup 支持更多的输出格式,包括ES模块、CommonJS、AMD、IIFE、UMD等,这使得 Rollup 更适合用于构建库。而Webpack 主要输出为自定义的模块格式,更适合构建应用程序。
  4. 开发服务器和热模块替换:Webpack 内置了开发服务器,并支持热模块替换(HMR),这对于开发环境非常有用。而Rollup则需要额外的插件或工具来提供这些功能。

上面所描述的点两者其实均可以做到,只是所侧重点不同。总的来说,Rollup和Webpack各有优势,选择哪一个取决于你的具体需求。

10. 如何在浏览器中运行 Rollup?

好吧,这个问题其实挺意外的,绝大部分情况都不会想在浏览器里面运行 Rollup 吧。

虽然常规的 Rollup 构建依赖于 Node 的一些特性,但 Rollup 也提供了一个用于在浏览器构建的版本,你可以通过以下方式安装它:

npm install @rollup/browser

在你的脚本中,通过以下方式导入它:

import { rollup } from '@rollup/browser';

或者,你可以从 CDN 导入,例如 ESM:

import * as rollup from 'https://unpkg.com/@rollup/browser/dist/es/rollup.browser.js';

UMD:

<script src="https://unpkg.com/@rollup/browser/dist/rollup.browser.js"></script>

这将创建一个全局变量 window.rollup。由于浏览器构建无法访问文件系统,因此需要提供解析和加载你想要打包的所有模块的插件。如下示例:

const modules = {
	'main.js': "import foo from 'foo.js'; console.log(foo);",
	'foo.js': 'export default 42;'
};

rollup
	.rollup({
		input: 'main.js',
		plugins: [
			{
				name: 'loader',
				resolveId(source) {
					if (modules.hasOwnProperty(source)) {
						return source;
					}
				},
				load(id) {
					if (modules.hasOwnProperty(id)) {
						return modules[id];
					}
				}
			}
		]
	})
	.then(bundle => bundle.generate({ format: 'es' }))
	.then(({ output }) => console.log(output[0].code));

这个例子支持了两个导入,main.jsfoo.js。下面这个例子是另一个使用绝对 URL 作为入口点并支持相对导入的例子。在这种情况下,我们只是重新打包 Rollup 本身,便可使它能用于任何其他 ESM 的URL:

rollup
	.rollup({
		input: 'https://unpkg.com/rollup/dist/es/rollup.js',
		plugins: [
			{
				name: 'url-resolver',
				resolveId(source, importer) {
					if (source[0] !== '.') {
						try {
							new URL(source);
							// If it is a valid URL, return it
							return source;
						} catch {
							// Otherwise make it external
							return { id: source, external: true };
						}
					}
					return new URL(source, importer).href;
				},
				async load(id) {
					const response = await fetch(id);
					return response.text();
				}
			}
		]
	})
	.then(bundle => bundle.generate({ format: 'es' }))
	.then(({ output }) => console.log(output));

写在最后

Rollup 在社区可以说一直是翘首,大量的库都在用 Rollup 打包,更不用说如今的红人 vite 了,更是以 Rollup 为底层。所以试着去了解一下也是蛮有价值的,当然,社区还有非常多的打包工具,包括 esbuild、tsup、gulp 等等。

不过万变不离其宗,其实打包工具底层基本都是一致的,只是可能在设计理念或者架构上有些差异,但始终绕不开这个范式,也很期待未来前端工程化发展能够有超越当前范式的模式和工具出现哇。

好吧,这是我从 LLM 学到,LLM 的出现就是 AI 在范式上的超越

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1827024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络协议,OSI,简单通信,IP和mac地址

认识协议 1.讲故事 2004年&#xff0c;小明因为给他爹打电话&#xff08;座机&#xff09;费用太贵&#xff0c;所以约定一种信号&#xff0c;响一次是报平安&#xff0c;响两次是要钱&#xff0c;响三次才需要接通。 2.概念 协议&#xff1a;是一种约定&#xff0c;这种约…

如何实现电脑监视员工的电脑屏幕?六个方法偷偷分享给你

实现电脑监视员工的电脑屏幕&#xff0c;通常需要借助专业的监控软件或系统&#xff0c;这些工具旨在帮助企业管理者监督员工的工作状态&#xff0c;确保工作效率&#xff0c;同时保护公司资产和数据安全。以下是几种常见的实现方式。 1. 使用专业的远程监控软件 安企神软件&a…

kaggle竞赛实战10——特征优化

特征优化思路&#xff1a; 在完成常规流程后&#xff0c;如果不知道怎么办&#xff0c;可以针对文本or时间序列特征进行进一步处理 首先&#xff0c;我们注意到&#xff0c;每一笔信用卡的交易记录都有交易时间&#xff0c;而对于时间字段和文本字段&#xff0c;普通的批量创…

解决用Three.js实现嘴型和语音同步时只能播放部分部位的问题 Three.js同时渲染播放多个组件变形动画的方法

前言 参考这篇文章ThreeJSChatGPT 实现前端3D数字人AI互动&#xff0c;前面搭后端、训练模型组内小伙伴都没有什么问题&#xff0c;到前端的时候&#xff0c;脸部就出问题了。看我是怎么解决的。 好文章啊&#xff0c;可惜百度前几个都找不到&#xff0c;o(╥﹏╥)o 问题情况 …

【C++】环境搭建及基本工作流程

C 当你需要写性能良好的代码时&#xff0c;C仍是不二选择 如果你想访问硬件、如果你想对硬件进行控制&#xff0c;C仍是首选。 所有的游戏引擎都是用C编写的&#xff0c;因为C可以直接控制硬件。 原理&#xff1a; C代码-->编译器编译comple-->目标平台的机器码-->放…

[算法刷题—二分法]寻找插入位置

题目展示: 本道题本身并不是很难,主要是学习和分析二分查找插入位置的方法。 首先大体上分为两种情况: 一.target在待查找的数组之中,返回对应值的下标索引。 二.target不在待查找的数组之中&#xff0c;需要返回target插入位置的索引(原数组有序) 第一种情况不难&#xff…

视图-什么是(VIEW)?怎么创建(CREATE VIEW)?怎么删除(DROP)?怎么用(SELECT/INSERT/UPDATE/DELETE)?

一、引言 之前对数据库的操作都是针对基本关系表&#xff0c;操作都是在数据库的全局逻辑模式上进行的&#xff0c;而在实际的数据库系统中&#xff0c;可能用户只关心或只被允许使用数据库中的某些基本关系表或基本关系表中的某些属性列&#xff0c;这些数据构成了数据库的外…

基于SVD的点云配准(下)

点云配准及特征提取详细解读 本篇博客将介绍一个用于点云配准的 C++ 代码示例,该示例使用 PCL(Point Cloud Library)库来处理和配准两个点云数据集。我们将逐步解析代码的关键部分,并解释每个步骤的作用。 代码说明 代码的整体结构及其主要功能: int main(int argc, ch…

Vue 状态管理:从Vuex到Pinia,Vue 3官方推荐的状态管理库深度解析

大家好&#xff0c;我是前端宝哥。 在编程界有句老话&#xff1a;“命名和缓存失效是世上两大难题。” 我得说&#xff0c;在现代Web应用的状态管理上&#xff0c;这难题得排第三&#xff01; 今天&#xff0c;咱们来深挖一下Vue的状态管理之道&#xff0c;并介绍一个超直观的解…

汽车IVI中控开发入门及进阶(二十八):视频SERDES芯片

前言: SerDes不是很常见,SerDes是将Ser和Des两种产品组合在一起的名称。Ser是Serializer或“并串转换器”的缩写,Des是Deserializer或“串并转换器”的简写。 Serdes是不是必须的?上一节介绍了camera,上上节也研究了video decoder,那么带摄像头的应用应该具体选哪个方案…

从踢足球到数字孪生

前言 贵州“村超”的火热现象是一个多方面因素共同作用的结果,它不仅是一场体育赛事,更是一个文化现象,反映了时代的精神和人民的情感诉求,同时也推动了乡村振兴和地区发展。足球的魅力是多方面的,它不仅仅是一项运动,更是一种全球性的文化现象。 简单规则下的无限变化:…

MySQL数据操作与查询- select 数据查询

一、select 选择列表 1、select基本结构 select 字段列表 from 表名 where 条件表达式 说明&#xff1a; &#xff08;1&#xff09; 必须的子句只有 select 子句和 from 子句。 &#xff08;2&#xff09;where 子句用于对查询结果进行过滤。 2、选择&#xff08;查询&am…

【会议征稿,IEEE出版】第六届物联网、自动化和人工智能国际学术会议(IoTAAI 2024,7月26-28)

第六届物联网、自动化和人工智能国际会议&#xff08;IoTAAI 2024&#xff09;将于2024年07月26-28日在中国广州召开。 会议旨在拓展国际科技学术交流渠道&#xff0c;搭建学术资源共享平台&#xff0c;促进全球范围内的科技创新&#xff0c;提升中外学术合作。会议还鼓励不同领…

再谈软件设计中的抽象思维(下),从FizzBuzz到规则引擎

作为《程序员的底层思维》出版两年之后的再回顾&#xff0c;在上一篇《再谈软件设计中的抽象思维&#xff08;上&#xff09;&#xff0c;从封装变化开始》中&#xff0c;我介绍了抽象设计的本质是发现变化点&#xff0c;结合问题域&#xff0c;提炼共性&#xff0c;沉淀领域知…

Golang | Leetcode Golang题解之第151题反转字符串中的单词

题目&#xff1a; 题解&#xff1a; import ("fmt" )func reverseWords(s string) string {//1.使用双指针删除冗余的空格slowIndex, fastIndex : 0, 0b : []byte(s)//删除头部冗余空格for len(b) > 0 && fastIndex < len(b) && b[fastIndex]…

建造者模式(大话设计模式)C/C++版本

建造者模式 C 参考&#xff1a;https://www.cnblogs.com/Galesaur-wcy/p/15907863.html #include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std;// Product Class&#xff0c;产品类&#xff0c;由多个…

【Java】Object类中的toString、equals方法

Object类 所有类都直接或间接的继承自Object类&#xff0c;Object类是所有Java类的根基类。 也就意味着所有的Java对象都拥有Object类的属性和方法。 如果在类的声明中未使用extends关键字指明其父类&#xff0c;则默认继承Object类。 toString()方法 【1】Object类的toStr…

植物大战僵尸杂交版 v2.0.88 mac版 Plants vs. Zombies 杂交版下载

特别注意&#xff1a;该游戏最低系统要求为macOS Sonoma 14.X&#xff0c;低于此系统版本的请勿下载&#xff01; 游戏介绍 植物大战僵尸杂交版是由B站UP主“潜艇伟伟迷”制作的一款结合了《植物大战僵尸》原有元素与创新玩法的游戏。这款游戏以其独特的“杂交”植物概念在B站…

【TypeScript】泛型工具

跟着 小满zs 学 ts&#xff1a;学习TypeScript24&#xff08;TS进阶用法-泛型工具&#xff09;_ts泛型工具-CSDN博客 Partial 所有属性可选的意思Required 所有属性必选的意思Pick 提取部分属性Exclude 排除部分属性emit 排除部分属性并且返回新的类型 Partial 属性变为可选。…

Python基础教程(二十):SMTP发送邮件

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…