Rollup 简介

Rollup 常用插件

掌握 Rollup 最常用的四个插件:node-resolve、commonjs、json 和 terser,理解插件的执行顺序。

🎯 引言

学完这篇文章,你将掌握 Rollup 最常用的四个官方插件,能够解析第三方依赖、转换 CommonJS 模块、导入 JSON 文件,并对产物进行压缩。


🧱 为什么需要插件

Rollup 的核心非常纯粹:只认识写在项目里的 ES 模块

但实际开发中,你经常会遇到这些情况:

  • import { debounce } from 'lodash-es' —— 依赖在 node_modules 里;
  • 某个老包是 CommonJS 写的,只有 module.exports
  • 想直接 import info from './package.json' 读取版本号;
  • 发布前想把代码压缩成一行。

这些需求都靠插件解决。Rollup 官方维护了一批高质量插件,名字统一以 @rollup/plugin- 开头。

本课程环境使用 Node.js v22.0.0,对应插件版本如下:

npm install @rollup/plugin-node-resolve@16.0.3 @rollup/plugin-commonjs@29.0.3 @rollup/plugin-json@6.1.0 @rollup/plugin-terser@1.0.0 --save-dev

下面逐个来看它们的用法。


🧰 node-resolve:解析第三方依赖

先看一个容易让人困惑的问题:明明已经 npm install lodash-es 了,为什么 Rollup 还是“找不到”它?

关键在于:npm install 只负责把文件下载到 node_modules 目录,而 Rollup 核心并不认识 node_modules 的查找规则。你的代码里其实有两种 import,走的是两套完全不同的规则:

// ① 相对路径:Rollup 核心认识,从当前文件出发直接就能找到
import { add } from './math.js';

// ② 裸导入(bare specifier):只是一个包名,没有路径信息,Rollup 核心不认识
import { debounce } from 'lodash-es';

第②种写法要找到真正的文件,需要一套专门的查找算法:

  1. node_modules 里定位 lodash-es 目录;
  2. 读取它的 package.json,根据 main / module / exports 字段判断入口文件是哪个(包里有几百个文件,到底加载哪一个?);
  3. 最终定位到具体文件,例如 node_modules/lodash-es/lodash.js

这套规则叫 Node.js 模块解析算法,Node.js 运行时内置了它,但 Rollup 核心刻意没有内置——保持核心精简,也因为 Rollup 的设计初衷是打包库,默认假定依赖是外部提供的。node-resolve 插件的作用,就是把这套查找算法补进 Rollup

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

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

加上它之后,lodash-es 就会被正常打进产物里。

不加插件时 Rollup 并不会直接报错,而是打印 (!) Unresolved dependencies 警告,并把 'lodash-es' 当作外部依赖原样保留在产物里。这样的产物在浏览器里运行会直接失败,因为浏览器同样不认识裸导入。所以只要引用了第三方包,记得加上 resolve()
你可能从没在 Webpack / Vite 里装过这类插件,因为它们把 node_modules 解析内置了。Rollup 选择把它做成插件,所以这一步需要你手动加上。

🧰 commonjs:转换 CommonJS 模块

npm 上很多老牌包是用 CommonJS 写的(module.exports = ...)。Rollup 只懂 ES 模块,遇到它们会报错。

commonjs 插件负责把 CommonJS 模块转换成 ES 模块

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

export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'es'
    },
    plugins: [resolve(), commonjs()]
};
commonjs 必须写在 resolve()后面。Rollup 按数组顺序执行插件,得先找到文件,才能转换它。顺序写反是新手最常踩的坑。

🧰 json:导入 JSON 文件

想在代码里直接读取 JSON 文件(比如 package.json 里的版本号),就用 json 插件:

rollup.config.mjs
import json from '@rollup/plugin-json';

export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'es'
    },
    plugins: [json()]
};

之后就可以像导入模块一样导入 JSON:

src/index.js
import pkg from '../package.json';

export function getVersion() {
    return pkg.version;
}

打包后 JSON 内容会变成普通的 JavaScript 对象,嵌进产物里。

json 插件只会打包你用到的字段。比如整个 package.json 有几十个字段,但你只用了 version,产物里就只会包含 version,其余字段会被 Tree Shaking 移除。

🧰 terser:压缩代码

发布前通常要把代码压缩,去掉空格注释、缩短变量名。terser 插件就是干这个的,它的用法比较特殊——写在 output.plugins,而不是顶层的 plugins

rollup.config.mjs
import terser from '@rollup/plugin-terser';

export default {
    input: 'src/index.js',
    output: [
        {
            file: 'dist/bundle.js',
            format: 'es'
        },
        {
            file: 'dist/bundle.min.js',
            format: 'es',
            plugins: [terser()]
        }
    ]
};

这样配置后会同时得到两份产物:一份正常代码(方便调试),一份压缩代码(发布给用户)。

压缩前后的对比非常明显:

dist/bundle.js
function add(a, b) {
    return a + b;
}

export { add };
dist/bundle.min.js
function n(n,t){return n+t}export{n as add};
为什么 terser 要写在 output.plugins?因为压缩发生在“产物生成之后”,属于输出阶段的操作。写在顶层 plugins 里虽然也能生效,但写在 output 里可以精确控制只压缩某一份产物。

🧾 小节总结

  • Rollup 核心只认 ES 模块,其余能力靠插件扩展,官方插件以 @rollup/plugin- 开头。
  • node-resolve 解析 node_modules 依赖,commonjs 转换 CommonJS 模块,二者通常搭配使用且 resolve 在前。
  • json 插件支持直接 import JSON 文件,未用到的字段会被摇掉。
  • terser 用于压缩代码,推荐写在 output.plugins 中,按产物分别控制。

❓ 知识问答

Q1:我已经 npm install 了,为什么打包时提示 Unresolved dependencies?

A:安装只是把文件放进了 node_modules,但 Rollup 核心不认识裸导入的查找规则,需要 node-resolve 插件来补这套算法。找到警告后,在 plugins 里加上 resolve() 即可。

Q2:所有第三方包都需要 commonjs 插件吗?

A:不是。现在越来越多的包直接提供 ES 模块版本(如 lodash-es),这种只需要 node-resolve。只有引用纯 CommonJS 的老包时才需要 commonjs

Q3:插件的执行顺序一定是从左到右吗?

A:是的。plugins 数组决定了执行顺序,记住口诀:先找到(resolve)、再转换(commonjs)、其他插件随后

Q4:压缩后的代码出错了好调试吗?

A:直接调试很困难,因为变量名都被缩短了。这就是上一篇讲的 sourcemap 的用武之地,配合 .map 文件就能映射回源码。

Q5:Rollup 有没有处理 CSS、图片的插件?

A:有,社区提供了 rollup-plugin-postcss@rollup/plugin-image 等插件。但写 JavaScript 库时很少用到,了解即可。


🧪 小练习

rollup-demo 加上插件支持:

  1. 安装本篇文章提到的四个插件(版本见文章开头的安装命令)。
  2. src/index.js 中导入 ../package.json 并导出一个 getName 函数返回包名。
  3. 配置 rollup.config.mjs,输出一份正常文件和一份 .min.js 压缩文件。
rollup.config.mjs
import json from '@rollup/plugin-json';
import terser from '@rollup/plugin-terser';

export default {
    input: 'src/index.js',
    output: [
        {
            file: 'dist/bundle.js',
            format: 'es'
        }
        // 请在这里补充 bundle.min.js 压缩产物
    ],
    plugins: [json()]
};

🎉 恭喜你已经掌握了 Rollup 的常用插件!下一篇我们会深入 Rollup 的招牌能力——Tree Shaking。