Skip to content

将 Rollup 与其他工具集成

使用 NPM 包

在某些时候,你的项目可能会依赖于从 NPM 安装到 node_modules 文件夹中的软件包。 与 Webpack 和 Browserify 等其他打包器不同,Rollup 不知道 "盒子外面" 如何处理这些依赖 - 我们需要添加一些配置。

At some point, it's likely that your project will depend on packages installed from NPM into your node_modules folder. Unlike other bundlers such as Webpack and Browserify, Rollup doesn't know "out of the box" how to handle these dependencies - we need to add some configuration.

让我们添加一个名为 答案 的简单依赖,它导出生命、宇宙和一切问题的答案:

Let's add a simple dependency called the-answer, which exports the answer to the question of life, the universe and everything:

shell
npm install the-answer
# or `npm i the-answer`
npm install the-answer
# or `npm i the-answer`

如果我们更新 src/main.js 文件……

If we update our src/main.js file…

js
// src/main.js
import answer from 'the-answer';

export default function () {
	console.log('the answer is ' + answer);
}
// src/main.js
import answer from 'the-answer';

export default function () {
	console.log('the answer is ' + answer);
}

…并运行 Rollup...

…and run Rollup…

shell
npm run build
npm run build

…我们会看到这样的警告:

…we'll see a warning like this:

(!) Unresolved dependencies
https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency
the-answer (imported by main.js)
(!) Unresolved dependencies
https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency
the-answer (imported by main.js)

生成的 bundle.js 仍然可以在 Node.js 中工作,因为 import 声明会变成 CommonJS require 语句,但 the-answer 不会包含在打包包中。 为此,我们需要一个插件。

The resulting bundle.js will still work in Node.js, because the import declaration gets turned into a CommonJS require statement, but the-answer does not get included in the bundle. For that, we need a plugin.

@rollup/plugin-node-resolve

@rollup/plugin-node-resolve 插件教 Rollup 如何查找外部模块。 安装它...

The @rollup/plugin-node-resolve plugin teaches Rollup how to find external modules. Install it…

shell
npm install --save-dev @rollup/plugin-node-resolve
npm install --save-dev @rollup/plugin-node-resolve

…并将其添加到你的配置文件中:

…and add it to your config file:

js
// 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.config.js
import resolve from '@rollup/plugin-node-resolve';

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

这次,当你 npm run build 时,不会触发任何警告 — 该打包包包含导入的模块。

This time, when you npm run build, no warning is emitted — the bundle contains the imported module.

@rollup/plugin-commonjs

一些库公开了可以按原样导入的 ES 模块 — the-answer 就是这样一个模块。 但目前,NPM 上的大多数包都以 CommonJS 模块的形式公开。 在此之前,我们需要将 CommonJS 转换为 ES2015,然后 Rollup 才能处理它们。

Some libraries expose ES modules that you can import as-is — the-answer is one such module. But at the moment, the majority of packages on NPM are exposed as CommonJS modules instead. Until that changes, we need to convert CommonJS to ES2015 before Rollup can process them.

@rollup/plugin-commonjs 插件正是这样做的。

The @rollup/plugin-commonjs plugin does exactly that.

请注意,大多数时候 @rollup/plugin-commonjs 应该位于转换模块的其他插件之前 — 这是为了防止其他插件做出破坏 CommonJS 检测的更改。 此规则的一个例外是 Babel 插件,如果你正在使用它,请将其放在 commonjs 插件之前。

Note that most of the time @rollup/plugin-commonjs should go before other plugins that transform your modules — this is to prevent other plugins from making changes that break the CommonJS detection. An exception for this rule is the Babel plugin, if you're using it then place it before the commonjs one.

对等依赖

假设你正在构建一个具有对等依赖的库,例如 React 或 Lodash。 如果你按照上述方式设置外部,你的 Rollup 将打包所有导入:

Let's say that you're building a library that has a peer dependency, such as React or Lodash. If you set up externals as described above, your rollup will bundle all imports:

js
import answer from 'the-answer';
import _ from 'lodash';
import answer from 'the-answer';
import _ from 'lodash';

你可以微调哪些导入被打包,哪些被视为外部导入。 对于此示例,我们将 lodash 视为外部,但不将 the-answer 视为外部。

You can finely tune which imports are bundled and which are treated as external. For this example, we'll treat lodash as external, but not the-answer.

这是配置文件:

Here is the config file:

js
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [
		resolve({
			// pass custom options to the resolve plugin
			moduleDirectories: ['node_modules']
		})
	],
	// indicate which modules should be treated as external
	external: ['lodash']
};
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [
		resolve({
			// pass custom options to the resolve plugin
			moduleDirectories: ['node_modules']
		})
	],
	// indicate which modules should be treated as external
	external: ['lodash']
};

瞧,lodash 现在将被视为外部,并且不会与你的库打包在一起。

Voilà, lodash will now be treated as external, and not be bundled with your library.

external 键接受模块名称数组,或者接受模块名称并返回 true(如果应将其视为外部)的函数。 例如:

The external key accepts either an array of module names, or a function which takes the module name and returns true if it should be treated as external. For example:

js
export default {
	// ...
	external: id => /lodash/.test(id)
};
export default {
	// ...
	external: id => /lodash/.test(id)
};

如果你使用 babel-插件-lodash 来挑选 lodash 模块,则可以使用此表单。 在这种情况下,Babel 会将你的 import 语句转换为如下所示:

You might use this form if you're using babel-plugin-lodash to cherry-pick lodash modules. In this case, Babel will convert your import statements to look like this:

js
import _merge from 'lodash/merge';
import _merge from 'lodash/merge';

external 的数组形式不处理通配符,因此此导入仅在函数形式中被视为外部。

The array form of external does not handle wildcards, so this import will only be treated as external in the functional form.

Babel

许多开发者在他们的项目中使用 Babel 是为了使用浏览器和 Node.js 尚不支持的最新 JavaScript 功能。

Many developers use Babel in their projects in order to use the latest JavaScript features that aren't yet supported by browsers and Node.js.

使用 Babel 和 Rollup 的最简单方法是使用 @rollup/plugin-babel。 首先,安装插件:

The easiest way to use both Babel and Rollup is with @rollup/plugin-babel. First, install the plugin:

shell
npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve
npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve

将其添加到 rollup.config.js

Add it to rollup.config.js:

js
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';

export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [resolve(), babel({ babelHelpers: 'bundled' })]
};
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';

export default {
	input: 'src/main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins: [resolve(), babel({ babelHelpers: 'bundled' })]
};

在 Babel 实际编译你的代码之前,需要对其进行配置。 创建一个新文件 src/.babelrc.json

Before Babel will actually compile your code, it needs to be configured. Create a new file, src/.babelrc.json:

json
{
	"presets": ["@babel/env"]
}
{
	"presets": ["@babel/env"]
}

我们将 .babelrc.json 文件放在 src 中,而不是项目根目录中。 这允许我们为测试等事情使用不同的 .babelrc.json(如果我们稍后需要的话) – 有关项目作用域和文件相关配置的更多信息,请参阅 Babel 文档

We're putting our .babelrc.json file in src, rather than the project root. This allows us to have a different .babelrc.json for things like tests, if we need that later – See the Babel documentation for more information on both project wide and file relative configuration.

现在,在运行 rollup 之前,我们需要安装 babel-coreenv 预设:

Now, before we run rollup, we need to install babel-core and the env preset:

shell
npm i -D @babel/core @babel/preset-env
npm i -D @babel/core @babel/preset-env

现在运行 Rollup 将创建一个包 - 除非我们实际上没有使用任何 ES2015 功能。 让我们通过编辑 src/main.js 来改变它:

Running Rollup now will create a bundle - except we're not actually using any ES2015 features. Let's change that by editing src/main.js:

js
// src/main.js
import answer from 'the-answer';

export default () => {
	console.log(`the answer is ${answer}`);
};
// src/main.js
import answer from 'the-answer';

export default () => {
	console.log(`the answer is ${answer}`);
};

使用 npm run build 运行 Rollup,并检查打包包:

Run Rollup with npm run build, and check the bundle:

js
'use strict';

var index = 42;

var main = function () {
	console.log('the answer is ' + index);
};

module.exports = main;
'use strict';

var index = 42;

var main = function () {
	console.log('the answer is ' + index);
};

module.exports = main;

Gulp

Rollup 返回 gulp 可以理解的 Promise,因此集成相对轻松。

Rollup returns Promises which are understood by gulp so integration is relatively painless.

语法与配置文件非常相似,但属性分为与 JavaScript API 对应的两个不同操作:

The syntax is very similar to the configuration file, but the properties are split across two different operations corresponding to the JavaScript API:

js
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', () => {
	return rollup
		.rollup({
			input: './src/main.ts',
			plugins: [rollupTypescript()]
		})
		.then(bundle => {
			return bundle.write({
				file: './dist/library.js',
				format: 'umd',
				name: 'library',
				sourcemap: true
			});
		});
});
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', () => {
	return rollup
		.rollup({
			input: './src/main.ts',
			plugins: [rollupTypescript()]
		})
		.then(bundle => {
			return bundle.write({
				file: './dist/library.js',
				format: 'umd',
				name: 'library',
				sourcemap: true
			});
		});
});

你还可以使用 async/await 语法:

You may also use the async/await syntax:

js
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', async function () {
	const bundle = await rollup.rollup({
		input: './src/main.ts',
		plugins: [rollupTypescript()]
	});

	await bundle.write({
		file: './dist/library.js',
		format: 'umd',
		name: 'library',
		sourcemap: true
	});
});
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', async function () {
	const bundle = await rollup.rollup({
		input: './src/main.ts',
		plugins: [rollupTypescript()]
	});

	await bundle.write({
		file: './dist/library.js',
		format: 'umd',
		name: 'library',
		sourcemap: true
	});
});

Deno

如果你喜欢在 Deno 中运行 Rollup,你可以像这样使用 esm.sh

If you like to run Rollup in Deno you can use esm.sh like so:

js
import {rollup} from "https://esm.sh/rollup@2.61.1";

const bundle = await rollup({ //...
import {rollup} from "https://esm.sh/rollup@2.61.1";

const bundle = await rollup({ //...

或者,你可以从 npm 安装 rollup 并使用 节点兼容层

Alternatively you can install rollup from npm and use the node compatibility layer:

js
import {createRequire} from "https://deno.land/std@0.110.0/node/module.ts";
const require = createRequire(import.meta.url);
const {rollup} = require("rollup");

const bundle = await rollup({ //...
import {createRequire} from "https://deno.land/std@0.110.0/node/module.ts";
const require = createRequire(import.meta.url);
const {rollup} = require("rollup");

const bundle = await rollup({ //...

请务必使用 --unstable 标志运行 deno。 如果你打算使用 bundle.write(),请不要忘记 --allow-read--allow-write

Be sure to run deno with the --unstable flag. And don't forget --allow-read and --allow-write if you plan on using bundle.write().