Skip to content

教程

创建你的第一个打包包

¥Creating Your First Bundle

在开始之前,你需要安装 Node.js 才能使用 NPM。你还需要知道如何访问计算机上的 命令行

¥Before we begin, you'll need to have Node.js installed so that you can use NPM. You'll also need to know how to access the command line on your machine.

使用 Rollup 最简单的方法是通过命令行接口(或 CLI)。现在,我们将在全局安装它(稍后我们将学习如何将它本地安装到你的项目中,以便你的构建过程是可移植的,但现在不用担心)。在命令行中输入:

¥The easiest way to use Rollup is via the Command Line Interface (or CLI). For now, we'll install it globally (later on we'll learn how to install it locally to your project so that your build process is portable, but don't worry about that yet). Type this into the command line:

shell
npm install rollup --global
# or `npm i rollup -g` for short

你现在可以运行 rollup 命令。尝试一下!

¥You can now run the rollup command. Try it!

shell
rollup

由于没有传递任何参数,Rollup 会打印使用说明。这与运行 rollup --helprollup -h 相同。

¥Because no arguments were passed, Rollup prints usage instructions. This is the same as running rollup --help, or rollup -h.

让我们创建一个简单的项目:

¥Let's create a simple project:

shell
mkdir -p my-rollup-project/src
cd my-rollup-project

首先,我们需要一个入口点。将其粘贴到名为 src/main.js 的新文件中:

¥First, we need an entry point. Paste this into a new file called src/main.js:

js
// src/main.js
import foo from './foo.js';
export default function () {
	console.log(foo);
}

然后,让我们创建入口点导入的 foo.js 模块:

¥Then, let's create the foo.js module that our entry point imports:

js
// src/foo.js
export default 'hello world!';

现在我们准备创建一个包:

¥Now we're ready to create a bundle:

shell
rollup src/main.js -f cjs

-f 选项(--format 的缩写)指定我们要创建的包类型 - 在本例中为 CommonJS(将在 Node.js 中运行)。因为我们没有指定输出文件,所以它将直接打印到 stdout

¥The -f option (short for --format) specifies what kind of bundle we're creating — in this case, CommonJS (which will run in Node.js). Because we didn't specify an output file, it will be printed straight to stdout:

js
'use strict';

const foo = 'hello world!';

const main = function () {
	console.log(foo);
};

module.exports = main;

你可以将包保存为文件,如下所示:

¥You can save the bundle as a file like so:

shell
rollup src/main.js -o bundle.js -f cjs

(你也可以执行 rollup src/main.js -f cjs > bundle.js,但正如我们稍后将看到的,如果你要生成源映射,则这不太灵活。)

¥(You could also do rollup src/main.js -f cjs > bundle.js, but as we'll see later, this is less flexible if you're generating sourcemaps.)

尝试运行代码:

¥Try running the code:

node
> var myBundle = require('./bundle.js');
> myBundle();
'hello world!'

恭喜!你已经使用 Rollup 创建了第一个打包包。

¥Congratulations! You've created your first bundle with Rollup.

使用配置文件

¥Using Config Files

到目前为止,一切都很好,但是当我们开始添加更多选项时,键入命令就变得有点麻烦。

¥So far, so good, but as we start adding more options it becomes a bit of a nuisance to type out the command.

为了避免重复,我们可以创建一个包含我们需要的所有选项的配置文件。配置文件是用 JavaScript 编写的,比原始 CLI 更灵活。

¥To save repeating ourselves, we can create a config file containing all the options we need. A config file is written in JavaScript and is more flexible than the raw CLI.

在项目根目录下创建一个名为 rollup.config.mjs 的文件,并添加以下代码:

¥Create a file in the project root called rollup.config.mjs, and add the following code:

js
// rollup.config.mjs
export default {
	
input
: 'src/main.js',
output
: {
file
: 'bundle.js',
format
: 'cjs'
} };

(请注意,你可以使用 CJS 模块,因此 module.exports = {/* config */}

¥(Note that you can use CJS modules and therefore module.exports = {/* config */})

要使用配置文件,我们使用 --config-c 标志:

¥To use the config file, we use the --config or -c flag:

shell
rm bundle.js # so we can check the command works!
rollup -c

你可以使用等效的命令行选项覆盖配置文件中的任何选项:

¥You can override any of the options in the config file with the equivalent command line options:

shell
rollup -c -o bundle-2.js # `-o` is equivalent to `--file` (formerly "output")

注意:Rollup 本身会处理配置文件,这就是我们能够使用 export default 语法的原因 - 代码不会使用 Babel 或类似的东西进行转译,因此你只能使用 Node.js 版本支持的 ES2015 功能 你正在运行。

¥Note: Rollup itself processes the config file, which is why we're able to use export default syntax – the code isn't being transpiled with Babel or anything similar, so you can only use ES2015 features that are supported in the version of Node.js that you're running.

如果你愿意,你可以指定与默认 rollup.config.mjs 不同的配置文件:

¥You can, if you like, specify a different config file from the default rollup.config.mjs:

shell
rollup --config rollup.config.dev.mjs
rollup --config rollup.config.prod.mjs

在本地安装 Rollup

¥Installing Rollup locally

在团队或分布式环境中工作时,明智的做法是将 Rollup 添加为本地依赖。在本地安装 Rollup 可以避免多个贡献者单独安装 Rollup 作为额外步骤的要求,并确保所有贡献者都使用相同版本的 Rollup。

¥When working within teams or distributed environments it can be wise to add Rollup as a local dependency. Installing Rollup locally prevents the requirement that multiple contributors install Rollup separately as an extra step, and ensures that all contributors are using the same version of Rollup.

要使用 NPM 在本地安装 Rollup:

¥To install Rollup locally with NPM:

shell
npm install rollup --save-dev

或者用 Yarn:

¥Or with Yarn:

shell
yarn -D add rollup

安装后,Rollup 可以在项目的根目录中运行:

¥After installing, Rollup can be run within the root directory of your project:

shell
npx rollup --config

或者用 Yarn:

¥Or with Yarn:

shell
yarn rollup --config

安装后,通常的做法是向 package.json 添加单个构建脚本,为所有贡献者提供方便的命令。例如

¥Once installed, it's common practice to add a single build script to package.json, providing a convenient command for all contributors. e.g.

json
{
	"scripts": {
		"build": "rollup --config"
	}
}

注意:一旦安装在本地,NPM 和 Yarn 都会解析依赖的 bin 文件,并在从包脚本调用时执行 Rollup。

¥Note: Once installed locally, both NPM and Yarn will resolve the dependency's bin file and execute Rollup when called from a package script.

使用插件

¥Using plugins

到目前为止,我们已经从入口点创建了一个简单的包,并通过相对路径导入了一个模块。当你构建更复杂的打包包时,你通常需要更大的灵活性 - 导入使用 NPM 安装的模块、使用 Babel 编译代码、使用 JSON 文件等等。

¥So far, we've created a simple bundle from an entry point and a module imported via a relative path. As you build more complex bundles, you'll often need more flexibility – importing modules installed with NPM, compiling code with Babel, working with JSON files and so on.

为此,我们使用插件来改变 Rollup 在打包过程中关键点的行为。Rollup 精彩列表 上维护着一份很棒的插件列表。

¥For that, we use plugins, which change the behaviour of Rollup at key points in the bundling process. A list of awesome plugins is maintained on the Rollup Awesome List.

在本教程中,我们将使用 @rollup/plugin-json,它允许 Rollup 从 JSON 文件导入数据。

¥For this tutorial, we'll use @rollup/plugin-json, which allows Rollup to import data from a JSON file.

在项目根目录下创建一个名为 package.json 的文件,并添加以下内容:

¥Create a file in the project root called package.json, and add the following content:

json
{
	"name": "rollup-tutorial",
	"version": "1.0.0",
	"scripts": {
		"build": "rollup -c"
	}
}

安装 @rollup/plugin-json 作为开发依赖:

¥Install @rollup/plugin-json as a development dependency:

shell
npm install --save-dev @rollup/plugin-json

(我们使用 --save-dev 而不是 --save,因为我们的代码在运行时实际上并不依赖于插件 - 仅当我们构建打包包时。)

¥(We're using --save-dev rather than --save because our code doesn't actually depend on the plugin when it runs – only when we're building the bundle.)

更新你的 src/main.js 文件,以便它从 package.json 而不是 src/foo.js 导入:

¥Update your src/main.js file so that it imports from your package.json instead of src/foo.js:

js
// src/main.js
import { version } from '../package.json';

export default function () {
	console.log('version ' + version);
}

编辑 rollup.config.mjs 文件以包含 JSON 插件:

¥Edit your rollup.config.mjs file to include the JSON plugin:

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

使用 npm run build 运行 Rollup。结果应该是这样的:

¥Run Rollup with npm run build. The result should look like this:

js
'use strict';

var version = '1.0.0';

function main() {
	console.log('version ' + version);
}

module.exports = main;

注意:仅导入我们实际需要的数据 - namedevDependencies 以及 package.json 的其他部分被忽略。这就是摇树效应。

¥Note: Only the data we actually need gets imported – name and devDependencies and other parts of package.json are ignored. That's tree-shaking in action.

使用输出插件

¥Using output plugins

某些插件还可以专门应用于某些输出。请参阅 插件钩子 了解输出特定插件功能的技术细节。简而言之,这些插件只能在 Rollup 的主要分析完成后才能修改代码。如果不兼容的插件被用作特定于输出的插件,Rollup 将触发警告。一种可能的用例是缩小浏览器中使用的包。

¥Some plugins can also be applied specifically to some outputs. See plugin hooks for the technical details of what output-specific plugins can do. In a nut-shell, those plugins can only modify code after the main analysis of Rollup has completed. Rollup will warn if an incompatible plugin is used as an output-specific plugin. One possible use-case is minification of bundles to be consumed in a browser.

让我们扩展前面的示例,以提供缩小的构建和非缩小的构建。为此,我们安装 @rollup/plugin-terser

¥Let us extend the previous example to provide a minified build together with the non-minified one. To that end, we install @rollup/plugin-terser:

shell
npm install --save-dev @rollup/plugin-terser

编辑 rollup.config.mjs 文件以添加第二个缩小的输出。作为格式,我们选择 iife。这种格式封装代码,以便可以通过浏览器中的 script 标记使用它,同时避免与其他代码发生不必要的交互。由于我们有导出,我们需要提供将由我们的包创建的全局变量的名称,以便其他代码可以通过此变量访问我们的导出。

¥Edit your rollup.config.mjs file to add a second minified output. As format, we choose iife. This format wraps the code so that it can be consumed via a script tag in the browser while avoiding unwanted interactions with other code. As we have an export, we need to provide the name of a global variable that will be created by our bundle so that other code can access our export via this variable.

js
// rollup.config.mjs
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
()]
};

除了 bundle.js 之外,Rollup 现在将创建第二个文件 bundle.min.js

¥Besides bundle.js, Rollup will now create a second file bundle.min.js:

js
var version = (function () {
	'use strict';
	var n = '1.0.0';
	return function () {
		console.log('version ' + n);
	};
})();

代码分割

¥Code Splitting

对于代码分割,在某些情况下,Rollup 会自动将代码分割成块,例如动态加载或多个入口点,并且有一种方法可以通过 output.manualChunks 选项显式告诉 Rollup 将哪些模块分割成单独的块。

¥For code splitting, there are cases where Rollup splits code into chunks automatically, like dynamic loading or multiple entry points, and there is a way to explicitly tell Rollup which modules to split into separate chunks via the output.manualChunks option.

为了使用代码分割功能来实现延迟动态加载(某些导入的模块仅在执行函数后才加载),我们回到原始示例并修改 src/main.js 以动态而不是静态加载 src/foo.js

¥To use the code splitting feature to achieve the lazy dynamic loading (where some imported module(s) is only loaded after executing a function), we go back to the original example and modify src/main.js to load src/foo.js dynamically instead of statically:

js
// src/main.js
export default function () {
	import('./foo.js').then(({ default: foo }) => console.log(foo));
}

Rollup 将使用动态导入来创建仅按需加载的单独块。为了让 Rollup 知道在哪里放置第二个块,我们没有传递 --file 选项,而是使用 --dir 选项设置要输出的文件夹:

¥Rollup will use the dynamic import to create a separate chunk that is only loaded on demand. In order for Rollup to know where to place the second chunk, instead of passing the --file option we set a folder to output to with the --dir option:

shell
rollup src/main.js -f cjs -d dist

这将创建一个文件夹 dist,其中包含两个文件 main.jschunk-[hash].js,其中 [hash] 是基于内容的哈希字符串。你可以通过指定 output.chunkFileNamesoutput.entryFileNames 选项来提供自己的命名模式。

¥This will create a folder dist containing two files, main.js and chunk-[hash].js, where [hash] is a content based hash string. You can supply your own naming patterns by specifying the output.chunkFileNames and output.entryFileNames options.

你仍然可以像以前一样使用相同的输出运行代码,尽管速度会慢一些,因为只有在我们第一次调用导出函数时才会开始加载和解析 ./foo.js

¥You can still run your code as before with the same output, albeit a little slower as loading and parsing of ./foo.js will only commence once we call the exported function for the first time.

shell
node -e "require('./dist/main.js')()"

如果我们不使用 --dir 选项,Rollup 将再次将块打印到 stdout,并添加注释以高亮块边界:

¥If we do not use the --dir option, Rollup will again print the chunks to stdout, adding comments to highlight the chunk boundaries:

js
//→ main.js:
'use strict';

function main() {
	Promise.resolve(require('./chunk-b8774ea3.js')).then(({ default: foo }) =>
		console.log(foo)
	);
}

module.exports = main;

//→ chunk-b8774ea3.js:
('use strict');

var foo = 'hello world!';

exports.default = foo;

如果你只想在使用昂贵的功能时加载和解析它们,那么这非常有用。

¥This is useful if you want to load and parse expensive features only once they are used.

代码分割的另一个用途是能够指定共享某些依赖的多个入口点。我们再次扩展示例以添加第二个入口点 src/main2.js,它静态导入 src/foo.js,就像我们在原始示例中所做的那样:

¥A different use for code-splitting is the ability to specify several entry points that share some dependencies. Again we extend our example to add a second entry point src/main2.js that statically imports src/foo.js just like we did in the original example:

js
// src/main2.js
import foo from './foo.js';
export default function () {
	console.log(foo);
}

如果我们向 rollup 提供两个入口点,则会创建三个块:

¥If we supply both entry points to rollup, three chunks are created:

shell
rollup src/main.js src/main2.js -f cjs

将输出

¥will output

js
//→ main.js:
'use strict';

function main() {
	Promise.resolve(require('./chunk-b8774ea3.js')).then(({ default: foo }) =>
		console.log(foo)
	);
}

module.exports = main;

//→ main2.js:
('use strict');

var foo_js = require('./chunk-b8774ea3.js');

function main2() {
	console.log(foo_js.default);
}

module.exports = main2;

//→ chunk-b8774ea3.js:
('use strict');

var foo = 'hello world!';

exports.default = foo;

请注意两个入口点如何导入相同的共享块。Rollup 永远不会重复代码,而是创建额外的块以仅加载必要的最低限度。同样,传递 --dir 选项会将文件写入磁盘。

¥Notice how both entry points import the same shared chunk. Rollup will never duplicate code and instead create additional chunks to only ever load the bare minimum necessary. Again, passing the --dir option will write the files to disk.

你可以通过原生 ES 模块、AMD 加载程序或 SystemJS 为浏览器构建相同的代码。

¥You can build the same code for the browser via native ES modules, an AMD loader or SystemJS.

例如,对于原生模块使用 -f es

¥For example, with -f es for native modules:

shell
rollup src/main.js src/main2.js -f es -d dist
html
<!doctype html>
<script type="module">
	import main2 from './dist/main2.js';
	main2();
</script>

或者,对于带有 -f system 的 SystemJS:

¥Or alternatively, for SystemJS with -f system:

shell
rollup src/main.js src/main2.js -f system -d dist

通过安装 SystemJS

¥Install SystemJS via

shell
npm install --save-dev systemjs

然后根据需要加载 HTML 页面中的一个或两个入口点:

¥And then load either or both entry points in an HTML page as needed:

html
<!doctype html>
<script src="node_modules/systemjs/dist/s.min.js"></script>
<script>
	System.import('./dist/main2.js').then(({ default: main }) => main());
</script>

请参阅 rollup-starter-code-splitting,了解如何设置一个 Web 应用,该应用在支持它们的浏览器上使用原生 ES 模块,并在必要时回退到 SystemJS。

¥See rollup-starter-code-splitting for an example on how to set up a web app that uses native ES modules on browsers that support them with a fallback to SystemJS if necessary.