Rollup
大约 8 分钟
前言
rollup是一个 JavaScript 打包工具。
 它使用JavaScript的ES6版本中包含的新标准化代码模块格式,而不是以前的CommonJS的AMD等特殊解决方案。
rollup优点
- rollup支持许多输出格式:ES模块、CommonJS、UMD、SystemJS等,不仅可以为web打包,还可以为许多其它平台打包。
 - 使用了Tree-shaking。
 - 虽然使用到了ES6新标准化代码的模块格式,但是也支持之前的CommonJS使用。
 - 根据不同的入口点和动态导入将代码拆分,只需适合用输出格式的导入机制,无需使用自定义加载器代码。
 - 强大的插件机制
 - vite打包也是使用的rollup
 
安装
npm install --global rollup
配置文件rollup.config.js
基本使用
export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	}
};
通常配置文件以rollup.config.js或rollup.config.mjs命名,它位于项目的根目录中。
 除非使用--configPlugin或--bundleConfigAsCjs选项,否则rollup将直接使用Node导入该文件。
 因为rollup遵循Node ESM语义,若想使用require导入模块或module.exports导出模块,使用这类的CommonJS模块的配置文件,需要将文件的扩展名/后缀名改为.cjs。
如果想支持ts语法,可以安装插件@rollup/plugin-typescript
配置项
查看详情
// rollup.config.js
// 可以是数组(即多个输入源)
export default {
	// 核心输入选项
	external,
	input, // 有条件地需要
	plugins,
	// 进阶输入选项
	cache,
	onwarn,
	preserveEntrySignatures,
	strictDeprecations,
	// 危险区域
	acorn,
	acornInjectPlugins,
	context,
	moduleContext,
	preserveSymlinks,
	shimMissingExports,
	treeshake,
	// 实验性
	experimentalCacheExpiry,
	experimentalLogSideEffects,
	perf,
	// 必需(可以是数组,用于描述多个输出)
	output: {
		// 核心输出选项
		dir,
		file,
		format,
		globals,
		name,
		plugins,
		// 进阶输出选项
		assetFileNames,
		banner,
		chunkFileNames,
		compact,
		entryFileNames,
		extend,
		footer,
		hoistTransitiveImports,
		inlineDynamicImports,
		interop,
		intro,
		manualChunks,
		minifyInternalExports,
		outro,
		paths,
		preserveModules,
		preserveModulesRoot,
		sourcemap,
		sourcemapBaseUrl,
		sourcemapExcludeSources,
		sourcemapFile,
		sourcemapIgnoreList,
		sourcemapPathTransform,
		validate,
		// 危险区域
		amd,
		esModule,
		exports,
		externalLiveBindings,
		freeze,
		indent,
		namespaceToStringTag,
		noConflict,
		preferConst,
		sanitizeFileName,
		strict,
		systemNullSetters,
		// 实验性
		experimentalMinChunkSize
	},
	watch: {
		buildDelay,
		chokidar,
		clearScreen,
		skipWrite,
		exclude,
		include
	}
};
配置rollup时有智能提示
方法一 由于rollup随附ts类型定义,因此可以使用JSDoc类型提示来利用IDE智能感知功能:
// rollup.config.js
/**
 * @type {import('rollup').RollupOptions}
 */
const config = {
	/* 你的配置 */
};
export default config;
方法二 依靠defineConfig辅助函数,无需JSDoc注释即可使用智能感知功能:
// rollup.config.js
import { defineConfig } from 'rollup';
export default defineConfig({
	/* 你的配置 */
});
方法三 依靠导入ts类型声明
import type { RollupOptions } from 'rollup';
const config: RollupOptions = {
	/* 你的配置 */
};
export default config;
使用原生Node ES模块时得注意事项
获取当前目录
对于CommonJS文件,以前经常使用__dirname访问当前目录并将相对路径解析为绝对路径。 这在原生ES模块中不被支持,所以建议以下方法:
// rollup.config.js
import { fileURLToPath } from 'node:url'
export default {
  ...,
  // 为 <currentdir>/src/some-file.js 生成绝对路径
  external: [fileURLToPath(new URL('src/some-file.js', import.meta.url))]
};
导入package.json文件
- node 17.5+,可以使用导入断言
 
import pkg from './package.json' assert { type: 'json' };
export default {
	// Mark package dependencies as "external". Rest of configuration
	// omitted.
	external: Object.keys(pkg.dependencies)
};
- 对于旧一些得Node版本,可以使用createRequire
 
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const pkg = require('./package.json');
// ...
- 直接从磁盘中读取并解析内容
 
// rollup.config.mjs
import { readFileSync } from 'node:fs';
// 使用 import.meta.url 可以使路径相对于当前源文件而不是 process.cwd()。
// 更多信息参见:
// https://nodejs.org/docs/latest-v16.x/api/esm.html#importmetaurl
const packageJson = JSON.parse(
	readFileSync(new URL('./package.json', import.meta.url))
);
// ...
命令行标志
查看详情
-c, --config <filename>     使用此配置文件
														(如果使用参数但未指定值,则默认为 rollup.config.js)
-d, --dir <dirname>         用于块的目录(如果不存在,则打印到 stdout)
-e, --external <ids>        排除模块 ID 的逗号分隔列表
-f, --format <format>       输出类型(amd、cjs、es、iife、umd、system)
-g, --globals <pairs>       `moduleID:Global` 对的逗号分隔列表
-h, --help                  显示此帮助消息
-i, --input <filename>      输入(替代 <entry file>)
-m, --sourcemap             生成源映射(`-m inline` 为内联映射)
-n, --name <name>           UMD 导出的名称
-o, --file <output>         单个输出文件(如果不存在,则打印到 stdout)
-p, --plugin <plugin>       使用指定的插件(可重复)
-v, --version               显示版本号
-w, --watch                 监视产物文件并在更改时重新构建
--amd.autoId                基于块名称生成 AMD ID
--amd.basePath <prefix>     要预先添加到自动生成的 AMD ID 的路径
--amd.define <name>         在 `define` 位置使用的函数
--amd.forceJsExtensionForImports 在 AMD 导入中使用 `.js` 扩展名
--amd.id <id>               AMD 模块的 ID(默认为匿名)
--assetFileNames <pattern>  发布资源的名称模式
--banner <text>             在产物顶部插入的代码(位于包装器之外)
--chunkFileNames <pattern>  发布次要块的名称模式
--compact                   缩小包装器代码
--context <variable>        指定顶级 `this` 值
--no-dynamicImportInCjs     将外部动态 CommonJS 导入编写为 require
--entryFileNames <pattern>  发布入口块的名称模式
--environment <values>      传递给配置文件的设置(请参阅示例)
--no-esModule               不添加 __esModule 属性
--exports <mode>            指定导出模式(auto、default、named、none)
--extend                    扩展由 --name 定义的全局变量
--no-externalImportAssertions 在 "es" 输出中省略导入断言
--no-externalLiveBindings   不生成支持实时绑定的代码
--failAfterWarnings         如果生成的构建产生警告,则退出并显示错误
--footer <text>             在产物底部插入的代码(位于包装器之外)
--no-freeze                 不冻结命名空间对象
--generatedCode <preset>    使用哪些代码特性(es5/es2015)
--generatedCode.arrowFunctions 在生成的代码中使用箭头函数
--generatedCode.constBindings 在生成的代码中使用 "const"
--generatedCode.objectShorthand 在生成的代码中使用简写属性
--no-generatedCode.reservedNamesAsProps 始终引用保留名称作为 props
--generatedCode.symbols     在生成的代码中使用符号
--no-hoistTransitiveImports 不将中转导入提升到入口块中
--no-indent                 不缩进结果
--inlineDynamicImports      使用动态导入时创建单次打包
--no-interop                不包括交互操作块
--intro <text>              在产物顶部插入的代码(位于包装器内部)
--no-makeAbsoluteExternalsRelative 不规范化外部导入
--maxParallelFileOps <value> 并行读取的文件数
--minifyInternalExports     强制或禁用内部导出的缩小
--noConflict                为 UMD 全局生成 noConflict 方法
--outro <text>              在产物底部插入的代码(位于包装器内部)
--perf                      显示性能计时
--no-preserveEntrySignatures 避免入口点的门面块
--preserveModules           保留模块结构
--preserveModulesRoot       将保留的模块放置在根路径下的此路径下
--preserveSymlinks          解析文件时不要跟随符号链接
--no-sanitizeFileName       不要替换文件名中的无效字符
--shimMissingExports        为丢失的导出创建卡扣变量
--silent                    不打印警告
--sourcemapBaseUrl <url>    使用给定的基本 URL 发出绝对源映射 URL
--sourcemapExcludeSources   在源映射中不包括源代码
--sourcemapFile <file>      指定源映射的包位置
--stdin=ext                 指定用于标准输入的文件扩展名
--no-stdin                  不要从 stdin 读取 "-"
--no-strict                 不在生成的模块中发出 `"use strict";`
--strictDeprecations        抛出有关不推荐使用的功能的错误
--no-systemNullSetters      不要将空的 SystemJS setter 替换为 `null`
--no-treeshake              禁用除屑优化
--no-treeshake.annotations 忽略纯调用注释
--treeshake.correctVarValueBeforeDeclaration 在声明之前将变量取消优化
--treeshake.manualPureFunctions <names> 手动将函数声明为纯函数
--no-treeshake.moduleSideEffects 假设模块没有副作用
--no-treeshake.propertyReadSideEffects 忽略属性访问副作用
--no-treeshake.tryCatchDeoptimization 不要关闭 try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects 假设未知的全局变量不会抛出异常
--validate                  验证输出
--waitForBundleInput        等待打包输入文件
--watch.buildDelay <number> 节流观察重建
--no-watch.clearScreen      重建时不要清除屏幕
--watch.exclude <files>     排除要观察的文件
--watch.include <files>     限制观察到指定文件
--watch.onBundleEnd <cmd>   在 "BUNDLE_END" 事件上运行的 Shell 命令
--watch.onBundleStart <cmd> 在 "BUNDLE_START" 事件上运行的 Shell 命令
--watch.onEnd <cmd>         在 "END" 事件上运行的 Shell 命令
--watch.onError <cmd>       在 "ERROR" 事件上运行的 Shell 命令
--watch.onStart <cmd>       在 "START" 事件上运行的 Shell 命令
--watch.skipWrite           在监视时不要将文件写入磁盘
rollup常用到的插件
@rollup/plugin-typescript:识别ts文件@rollup/plugin-json:允许导入json文件
// src/main.js
import { version } from '../package.json';
export default function () {
	console.log('version ' + version);
}
// rollup.config.js
import json from '@rollup/plugin-json';
export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [json()]
};
@rollup/plugin-terser:压缩配置插件
// rollup.config.js
import json from '@rollup/plugin-json';
import terser from '@rollup/plugin-terser';
export default {
	input: 'src/main.js',
	output: [
		{
			file: 'bundle.js',
			format: 'cjs'
		},
		{
			file: 'bundle.min.js',
			format: 'iife',
			name: 'version',
			plugins: [terser()]
		}
	],
	plugins: [json()]
};
@rollup/plugin-node-resolve:让rollup在打包过程中处理第三方插件
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [resolve()]
};
@rollup/plugin-commonjs:让rollup处理依赖中使用到commonjs的模块。@rollup/plugin-replace:可以用于区分生产环境还是开发环境,此插件搭配cross-env进行使用
import replace from '@rollup/plugin-replace';
export default {
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    })
  ]
};
cross-env:设置环境变量
{
  "scripts": {
    "serve": "cross-env NODE_ENV=development rollup -c -w",
    "build": "cross-env NODE_ENV=production  rollup -c"
  },
}
rollup-plugin-node-externals:自动排除package.json中一些模块为外部模块
import externals from "rollup-plugin-node-externals";
export default {
  plugins: [
    externals({
      devDeps: false,//自动将dependencies下的依赖设为外部模块
    }),
  ],
};
ES 模块语法
具名导入
使用源模块中的原始名进行导入:
import { something } from './module.js';
从源模块导入特定项目,并在导入时分配自定义名称:
import { something as somethingElse } from './module.js';
名称空间导入
将源模块中的所有内容作为一个对象导入,该对象将所有源模块的named exports公开为属性和方法。
import * as module from './module.js';
默认导入
源模块以export default导出
import something from './module.js';
无命名导入
加载模块代码,但不要使任何新对象可用:
import './module.js';
这对于polyfill很有用,或者当导入代码的主要目的是处理prototypes时
动态导入
import('./modules.js').then(({ default: DefaultExport, NamedExport }) => {
	// 用这个模块做点什么...
});
导出
具名导出
导出前先声明值
const something = true;
export { something };
导出时重命名:
export { something as somethingElse };
声明后立即导出值:
// `var`, `let`, `const`, `class`, 和 `function` 都是有效的
export const something = true;
默认导出
export default something;
