JavaScript?API調(diào)用Rollup打包流程快速上手
正文
Rollup是一款基于ESModule模塊規(guī)范實(shí)現(xiàn)的JavaScript打包工具,在前端社區(qū)中赫赫有名,同時(shí)也在Vite的架構(gòu)體系中發(fā)揮著重要作用。不僅是Vite生產(chǎn)環(huán)境下的打包工具,其插件機(jī)制也被Vite所兼容,可以說(shuō)是Vite的構(gòu)建基石。
接下來(lái),我們將圍繞Rollup的基本概念和核心特性展開(kāi),學(xué)習(xí)完本小節(jié)內(nèi)容,你不僅能知道Rollup是如何打包項(xiàng)目的,還能學(xué)會(huì)Rollup更高階的使用方式,甚至能夠通過(guò)JavaScriptAPI二次開(kāi)發(fā)Rollup。
一、快速上手
首先,創(chuàng)建一個(gè)空的文件夾,然后使用npm init -y新建一個(gè)項(xiàng)目,此時(shí),打開(kāi)項(xiàng)目會(huì)發(fā)現(xiàn)多了一個(gè)package.json文件。
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}接著,繼續(xù)安裝 rollup 依賴,命令如下。
npm i rollup
新增 src/index.js 和 src/util.js 和rollup.config.js 三個(gè)文件,目錄結(jié)構(gòu)如下所示。
.
├── package.json
├── pnpm-lock.yaml
├── rollup.config.js
└── src
├── index.js
└── util.js其中,index.js 和 util.js 和rollup.config.js 文件的內(nèi)容分別如下。
// src/index.js
import { add } from "./util";
console.log(add(1, 2));
// src/util.js
export const add = (a, b) => a + b;
export const multi = (a, b) => a * b;
// rollup.config.js
// 以下注釋是為了能使用 VSCode 的類型提示
/**
* @type { import('rollup').RollupOptions }
*/
const buildOptions = {
input: ["src/index.js"],
output: {
// 產(chǎn)物輸出目錄
dir: "dist/es",
// 產(chǎn)物格式
format: "esm",
},
};
export default buildOptions;然后,在package.json中加入如下的構(gòu)建腳本。
{
// rollup 打包命令,`-c` 表示使用配置文件中的配置
"build": "rollup -c"
}接著,在終端執(zhí)行一下npm run build,可以看到如下的命令行信息。

接下來(lái),我們打開(kāi)dist/es 目錄查看一下產(chǎn)物的內(nèi)容。
const add = (a, b) => a + b; console.log(add(1, 2));
可以發(fā)現(xiàn),util.js中的multi方法并沒(méi)有被打包到產(chǎn)物中,這是因?yàn)?Rollup 具有天然的 Tree Shaking 功能,可以分析出未使用到的模塊并自動(dòng)擦除。
所謂 Tree Shaking(搖樹(shù)),也是計(jì)算機(jī)編譯原理中DCE(Dead Code Elimination,即消除無(wú)用代碼) 技術(shù)的一種實(shí)現(xiàn)。由于 ES 模塊依賴關(guān)系是確定的,和運(yùn)行時(shí)狀態(tài)無(wú)關(guān)。因此 Rollup 可以在編譯階段分析出依賴關(guān)系,對(duì) AST 語(yǔ)法樹(shù)中沒(méi)有使用到的節(jié)點(diǎn)進(jìn)行刪除,從而實(shí)現(xiàn) Tree Shaking。
二、常用配置解讀
2.1 多產(chǎn)物配置
在打包 JavaScript 類庫(kù)的場(chǎng)景中,我們通常需要對(duì)外暴露出不同格式的產(chǎn)物供他人使用,不僅包括 ESM,也需要包括諸如CommonJS、UMD等格式,保證良好的兼容性。那么,同一份入口文件,如何讓 Rollup 給我們打包出不一樣格式的產(chǎn)物呢?為了實(shí)現(xiàn)這一需求,我們基于上述的配置文件來(lái)進(jìn)行如下修改。
// rollup.config.js
/**
* @type { import('rollup').RollupOptions }
*/
const buildOptions = {
input: ["src/index.js"],
// 將 output 改造成一個(gè)數(shù)組
output: [
{
dir: "dist/es",
format: "esm",
},
{
dir: "dist/cjs",
format: "cjs",
},
],
};
export default buildOptions;從代碼中可以看到,我們將output屬性配置成一個(gè)數(shù)組,數(shù)組中每個(gè)元素都是一個(gè)描述對(duì)象,決定了不同產(chǎn)物的輸出行為。
2.2 多入口配置
除了多產(chǎn)物配置,Rollup 中也支持多入口配置,而且通常情況下兩者會(huì)被結(jié)合起來(lái)使用。接下來(lái),就讓我們繼續(xù)改造之前的配置文件,將 input 設(shè)置為一個(gè)數(shù)組或者一個(gè)對(duì)象,如下所示。
{
input: ["src/index.js", "src/util.js"]
}
// 或者
{
input: {
index: "src/index.js",
util: "src/util.js",
},
}然后,再次執(zhí)行npm run build??梢园l(fā)現(xiàn),所有入口的不同格式產(chǎn)物已經(jīng)成功輸出。

如果不同入口對(duì)應(yīng)的打包配置不一樣,我們也可以使用默認(rèn)的配置來(lái)導(dǎo)出一個(gè)配置數(shù)組,如下所示。
// rollup.config.js
/**
* @type { import('rollup').RollupOptions }
*/
const buildIndexOptions = {
input: ["src/index.js"],
output: [
// 省略 output 配置
],
};
/**
* @type { import('rollup').RollupOptions }
*/
const buildUtilOptions = {
input: ["src/util.js"],
output: [
// 省略 output 配置
],
};
export default [buildIndexOptions, buildUtilOptions];如果是比較復(fù)雜的打包場(chǎng)景(如 Vite 源碼本身的打包),我們需要將項(xiàng)目的代碼分成幾個(gè)部分,用不同的 Rollup 配置分別打包。
2.3 自定義output配置
前面我們提到了input的使用,主要用來(lái)聲明入口,可以配置成字符串、數(shù)組或者對(duì)象,使用比較簡(jiǎn)單。而output與之相對(duì),用來(lái)配置輸出的相關(guān)信息,常用的配置項(xiàng)如下。
output: {
// 產(chǎn)物輸出目錄
dir: path.resolve(__dirname, 'dist'),
// 以下三個(gè)配置項(xiàng)都可以使用這些占位符:
// 1. [name]: 去除文件后綴后的文件名
// 2. [hash]: 根據(jù)文件名和文件內(nèi)容生成的 hash 值
// 3. [format]: 產(chǎn)物模塊格式,如 es、cjs
// 4. [extname]: 產(chǎn)物后綴名(帶`.`)
// 入口模塊的輸出文件名
entryFileNames: `[name].js`,
// 非入口模塊(如動(dòng)態(tài) import)的輸出文件名
chunkFileNames: 'chunk-[hash].js',
// 靜態(tài)資源文件輸出文件名
assetFileNames: 'assets/[name]-[hash][extname]',
// 產(chǎn)物輸出格式,包括`amd`、`cjs`、`es`、`iife`、`umd`、`system`
format: 'cjs',
// 是否生成 sourcemap 文件
sourcemap: true,
// 如果是打包出 iife/umd 格式,需要對(duì)外暴露出一個(gè)全局變量,通過(guò) name 配置變量名
name: 'MyBundle',
// 全局變量聲明
globals: {
// 項(xiàng)目中可以直接用`$`代替`jquery`
jquery: '$'
}
}2.4 依賴 external
對(duì)于某些第三方包,有時(shí)候我們不想讓 Rollup 進(jìn)行打包,也可以通過(guò) external 進(jìn)行外部化,配置如下。
{
external: ['react', 'react-dom']
}在 SSR 構(gòu)建或者使用 ESM CDN 的場(chǎng)景中,這個(gè)配置將非常有用
2.5 接入插件
在Rollup的日常使用中,我們難免會(huì)遇到一些Rollup本身不支持的場(chǎng)景,比如兼容CommonJS打包、注入環(huán)境變量、配置路徑別名、壓縮產(chǎn)物代碼等等。這個(gè)時(shí)候就需要我們引入相應(yīng)的Rollup插件了。接下來(lái)以一個(gè)具體的場(chǎng)景為例帶大家熟悉一下Rollup插件的使用。
雖然Rollup能夠打包輸出出CommonJS格式的產(chǎn)物,但對(duì)于輸入給Rollup的代碼并不支持CommonJS,僅僅支持ESM。你可能會(huì)說(shuō),那我們直接在項(xiàng)目中統(tǒng)一使用ESM規(guī)范就可以了啊,這有什么問(wèn)題呢?需要注意的是,我們不光要考慮項(xiàng)目本身的代碼,還要考慮第三方依賴。
目前為止,還是有不少第三方依賴只有CommonJS格式產(chǎn)物而并未提供ESM產(chǎn)物,比如項(xiàng)目中用到lodash時(shí),打包項(xiàng)目會(huì)出現(xiàn)這樣的報(bào)錯(cuò):

所以,我們需要引入額外的插件去解決這個(gè)問(wèn)題,我們需要安裝兩個(gè)核心的插件包。
pnpm i @rollup/plugin-node-resolve @rollup/plugin-commonjs
關(guān)于這兩個(gè)插件包的說(shuō)明如下:
- @rollup/plugin-node-resolve是為了允許我們加載第三方依賴,否則像import React from 'react' 的依賴導(dǎo)入語(yǔ)句將不會(huì)被 Rollup 識(shí)別。
- @rollup/plugin-commonjs 的作用是將 CommonJS 格式的代碼轉(zhuǎn)換為 ESM 格式
然后,我們?cè)谂渲梦募袑?dǎo)入這些插件,相關(guān)的配置如下:
// rollup.config.js
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
/**
* @type { import('rollup').RollupOptions }
*/
export default {
input: ["src/index.js"],
output: [
{
dir: "dist/es",
format: "esm",
},
{
dir: "dist/cjs",
format: "cjs",
},
],
// 通過(guò) plugins 參數(shù)添加插件
plugins: [resolve(), commonjs()],
};現(xiàn)在,我們以lodash這個(gè)只有 CommonJS 產(chǎn)物的第三方包為例測(cè)試一下。
npm i lodash
然后,在src/index.js 加入如下的代碼。
import { merge } from "lodash";
console.log(merge);然后,執(zhí)行 npm run build命令,就可以發(fā)現(xiàn)產(chǎn)物已經(jīng)正常生成了,如下圖所示。

在Rollup配置文件中,plugins除了可以與output配置在同一級(jí),也可以配置在output參數(shù)里面。
// rollup.config.js
import { terser } from 'rollup-plugin-terser'
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
output: {
// 加入 terser 插件,用來(lái)壓縮代碼
plugins: [terser()]
},
plugins: [resolve(), commonjs()]
}需要注意的是,output.plugins中配置的插件是有一定限制的,只有使用Output 階段相關(guān)鉤子的插件才能夠放到這個(gè)配置中,大家可以去這個(gè)站點(diǎn)查看 Rollup 的 Output 插件列表。這里也給大家分享其它一些比較常用的 Rollup 插件庫(kù):
- @rollup/plugin-json: 支持.json的加載,并配合rollup的Tree Shaking機(jī)制去掉未使用的部分,進(jìn)行按需打包。
- @rollup/plugin-babel:在 Rollup 中使用 Babel 進(jìn)行 JS 代碼的語(yǔ)法轉(zhuǎn)譯。
- @rollup/plugin-typescript: 支持使用 TypeScript 開(kāi)發(fā)。
- @rollup/plugin-alias:支持別名配置。
- @rollup/plugin-replace:在 Rollup 進(jìn)行變量字符串的替換。
- rollup-plugin-visualizer: 對(duì) Rollup 打包產(chǎn)物進(jìn)行分析,自動(dòng)生成產(chǎn)物體積可視化分析圖。
三、JavaScript API
我們通過(guò)Rollup的配置文件結(jié)合rollup -c完成了 Rollup 的打包過(guò)程,但有些場(chǎng)景下我們需要基于 Rollup 定制一些打包過(guò)程,配置文件就不夠靈活了,這時(shí)候我們需要用到對(duì)應(yīng) JavaScript API 來(lái)調(diào)用 Rollup,主要分為rollup.rollup和rollup.watch兩個(gè) API,接下來(lái)我們以具體的例子來(lái)學(xué)習(xí)一下。
首先,是 rollup.rollup,用來(lái)一次性地進(jìn)行 Rollup 打包,可以新建一個(gè)build.js文件,內(nèi)容如下。
// build.js
const rollup = require("rollup");
// 常用 inputOptions 配置
const inputOptions = {
input: "./src/index.js",
external: [],
plugins:[]
};
const outputOptionsList = [
// 常用 outputOptions 配置
{
dir: 'dist/es',
entryFileNames: `[name].[hash].js`,
chunkFileNames: 'chunk-[hash].js',
assetFileNames: 'assets/[name]-[hash][extname]',
format: 'es',
sourcemap: true,
globals: {
lodash: '_'
}
}
// 省略其它的輸出配置
];
async function build() {
let bundle;
let buildFailed = false;
try {
// 1. 調(diào)用 rollup.rollup 生成 bundle 對(duì)象
bundle = await rollup.rollup(inputOptions);
for (const outputOptions of outputOptionsList) {
// 2. 拿到 bundle 對(duì)象,根據(jù)每一份輸出配置,調(diào)用 generate 和 write 方法分別生成和寫(xiě)入產(chǎn)物
const { output } = await bundle.generate(outputOptions);
await bundle.write(outputOptions);
}
} catch (error) {
buildFailed = true;
console.error(error);
}
if (bundle) {
// 最后調(diào)用 bundle.close 方法結(jié)束打包
await bundle.close();
}
process.exit(buildFailed ? 1 : 0);
}
build();讓我們來(lái)解釋一下上面的代碼:
- 通過(guò) rollup.rollup方法,傳入 inputOptions,生成 bundle 對(duì)象;
- 調(diào)用 bundle 對(duì)象的 generate 和 write 方法,傳入outputOptions,分別完成產(chǎn)物和生成和磁盤(pán)寫(xiě)入。
- 調(diào)用 bundle 對(duì)象的 close 方法來(lái)結(jié)束打包。
接著,執(zhí)行node build.js命令。這樣,我們就可以完成了以編程的方式來(lái)調(diào)用 Rollup 打包的過(guò)程。除了通過(guò)rollup.rollup完成一次性打包,我們也可以通過(guò)rollup.watch來(lái)完成watch模式下的打包,即每次源文件變動(dòng)后自動(dòng)進(jìn)行重新打包。你可以新建watch.js文件,配置如下。
// watch.js
const rollup = require("rollup");
const watcher = rollup.watch({
// 和 rollup 配置文件中的屬性基本一致,只不過(guò)多了`watch`配置
input: "./src/index.js",
output: [
{
dir: "dist/es",
format: "esm",
},
{
dir: "dist/cjs",
format: "cjs",
},
],
watch: {
exclude: ["node_modules/**"],
include: ["src/**"],
},
});
// 監(jiān)聽(tīng) watch 各種事件
watcher.on("restart", () => {
console.log("重新構(gòu)建...");
});
watcher.on("change", (id) => {
console.log("發(fā)生變動(dòng)的模塊id: ", id);
});
watcher.on("event", (e) => {
if (e.code === "BUNDLE_END") {
console.log("打包信息:", e);
}
});現(xiàn)在,我們可以通過(guò)執(zhí)行node watch.js開(kāi)啟 Rollup 的 watch 打包模式,當(dāng)你改動(dòng)一個(gè)文件后可以看到如下的日志,說(shuō)明 Rollup 自動(dòng)進(jìn)行了重新打包,并觸發(fā)相應(yīng)的事件回調(diào)函數(shù)。
發(fā)生生變動(dòng)的模塊id: /xxx/src/index.js
重新構(gòu)建...
打包信息: {
code: 'BUNDLE_END',
duration: 10,
input: './src/index.js',
output: [
// 輸出產(chǎn)物路徑
],
result: {
cache: { /* 產(chǎn)物具體信息 */ },
close: [AsyncFunction: close],
closed: false,
generate: [AsyncFunction: generate],
watchFiles: [
// 監(jiān)聽(tīng)文件列表
],
write: [AsyncFunction: write]
}
}基于如上的兩個(gè) JavaScript API 我們可以很方便地在代碼中調(diào)用 Rollup 的打包流程,相比于配置文件有了更多的操作空間,你可以在代碼中通過(guò)這些 API 對(duì) Rollup 打包過(guò)程進(jìn)行定制,甚至是二次開(kāi)發(fā)。
以上就是JavaScript API調(diào)用Rollup打包流程快速上手的詳細(xì)內(nèi)容,更多關(guān)于JavaScript API調(diào)用Rollup打包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序開(kāi)發(fā)之相冊(cè)選擇和拍照詳解及實(shí)例代碼
這篇文章主要介紹了微信小程序開(kāi)發(fā)之相冊(cè)選擇和拍照詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
微信小程序中使用Promise進(jìn)行異步流程處理的實(shí)例詳解
這篇文章主要介紹了微信小程序中使用Promise進(jìn)行異步流程處理的實(shí)例詳解的相關(guān)資料,這里詳細(xì)說(shuō)明該如何使用Promise 來(lái)進(jìn)行異步流程的處理,提供具體實(shí)現(xiàn)步驟,需要的朋友可以參考下2017-08-08
JavaScript與JQuery框架基礎(chǔ)入門(mén)教程
這篇文章主要介紹了jQuery和JavaScript入門(mén)基礎(chǔ)知識(shí)學(xué)習(xí)指南,jQuery是當(dāng)下最主流人氣最高的JavaScript庫(kù),需要的朋友可以參考下2021-07-07
微信小程序開(kāi)發(fā)入門(mén)基礎(chǔ)教程
這篇文章主要介紹了微信小程序開(kāi)發(fā)入門(mén)基礎(chǔ)教程的相關(guān)資料,需要的朋友可以參考下2017-04-04
微信小程序 實(shí)現(xiàn)tabs選項(xiàng)卡效果實(shí)例代碼
這篇文章主要介紹了微信小程序 實(shí)現(xiàn)tabs選項(xiàng)卡效果實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10
基于JavaScript ES新特性let與const關(guān)鍵字
這篇文章主要介紹了基于JavaScript ES新特性let與const關(guān)鍵字,let是ECMAScript 2015新增的一個(gè)關(guān)鍵字,用于聲明變量,const關(guān)鍵字用于聲明一個(gè)常量,更多詳細(xì)內(nèi)容,請(qǐng)需要的小伙伴參考下面文章的介紹,希望對(duì)你有所幫助2021-12-12

