# output.manualChunks

类型:
{ [chunkAlias: string]: string[] } | ((id: string, {getModuleInfo, getModuleIds}) => string | void)

该选项允许你创建自定义的公共 chunk。当值为对象形式时,每个属性代表一个 chunk,其中包含列出的模块及其所有依赖,除非他们已经在其他 chunk 中,否则将会是模块图(module graph)的一部分。chunk 的名称由对象属性的键决定。

请注意,列出的模块本身不一定是模块图的一部分,该特性对于使用 @rollup/plugin-node-resolve 包并从中使用深度引用(deep imports)是非常有用的。例如:

({
	manualChunks: {
		lodash: ['lodash']
	}
});

上述例子中,即使你只是使用 import get from 'lodash/get' 形式引入,Rollup 也会将 lodash 的所有模块放到一个自定义 chunk 中。

当该选项值为函数形式时,每个被解析的模块都会经过该函数处理。如果函数返回字符串,那么该模块及其所有依赖将被添加到以返回字符串命名的自定义 chunk 中。例如,以下例子会创建一个命名为 vendor 的 chunk,它包含所有在 node_modules 中的依赖:

function manualChunks(id) {
	if (id.includes('node_modules')) {
		return 'vendor';
	}
}

请注意,如果自定义 chunk 在使用相应模块之前触发了副作用,那么它可能改变整个应用的行为。

当 manualChunks 值为函数形式时,它的第二个参数是一个对象,包含 getModuleInfo 函数和 getModuleIds 函数,其工作方式与插件上下文中的 this.getModuleInfo 和 this.getModuleIds 相同。

该选项可以用于根据模块在模块图中的位置动态确定它应该被放在哪个自定义 chunk 中。例如,考虑有这样一个场景,有一组组件,每个组件动态引入一组已转译的依赖,即:

// 在 “foo” 组件中

function getTranslatedStrings(currentLanguage) {
	switch (currentLanguage) {
		case 'en':
			return import('./foo.strings.en.js');
		case 'de':
			return import('./foo.strings.de.js');
		// ...
	}
}

如果有很多这样的组件一起使用,则会导致生成许多很小的动态引入 chunk:尽管我们知道由同一 chunk 引入的所有相同语言的语言文件将始终一起使用,但是 Rollup 并不知道。

下面代码将会合并所有仅由单个入口使用的相一语言文件:

function manualChunks(id, { getModuleInfo }) {
	const match = /.*\.strings\.(\w+)\.js/.exec(id);
	if (match) {
		const language = match[1]; // 例如 “en”
		const dependentEntryPoints = [];

		// 在这里,我们使用 Set 一次性处理每个依赖模块
		// 它可以阻止循环依赖中的无限循环
		const idsToHandle = new Set(getModuleInfo(id).dynamicImporters);

		for (const moduleId of idsToHandle) {
			const { isEntry, dynamicImporters, importers } =
				getModuleInfo(moduleId);
			if (isEntry || dynamicImporters.length > 0)
				dependentEntryPoints.push(moduleId);

			// Set 迭代器足够智能,可以处理
			// 在迭代过程中添加元素
			for (const importerId of importers) idsToHandle.add(importerId);
		}

		// 如果仅有一个入口,那么我们会根据入口名
		// 将它放到独立的 chunk 中
		if (dependentEntryPoints.length === 1) {
			return `${
				dependentEntryPoints[0].split('/').slice(-1)[0].split('.')[0]
			}.strings.${language}`;
		}
		// 对于多个入口,我们会把它放到“共享”的 chunk 中
		if (dependentEntryPoints.length > 1) {
			return `shared.strings.${language}`;
		}
	}
}
Last Updated: 6/14/2023, 8:56:23 AM