通常前端开发过程中,大家使用的打包工具都是 webpack 或 vite,但近期一个需求需要我开发一个js库。但是webpack在开发js库上并不是很适用。通过一番搜索,最终确定使用 rollup 来进行打包处理。
什么是 Rollup
Rollup 是一个用于 JavaScript 的模块打包工具,它将小的代码片段编译成更大、更复杂的代码,例如库或应用程序。
它的优点如下:
- 支持多种输入格式:ES Module、CommonJS、UMD、SystemJS等,打包后的bundle不仅适用于web,还适用于其他许多平台
- Tree-Shaking:静态分析你导入的代码,并将排除任何实际上没有使用的内容。这使你可以在现有的工具和模块的基础上构建,而不需要添加额外的依赖项或使项目的大小变得臃肿
- 强大的插件
- ……
下面,我们就来看看如何使用 rollup。
一、基础使用
- 首先我们创建一个文件夹 “rollup-test”,然后切换到该文件夹下执行项目初始化命令:
npm init --yes
- 根目录下创建 src 目录,并在该文件夹下创建
index.js文件,文件内容如下:
export function sayHelloWorld() {
console.log('hello world');
}
- 安装 rollup 以及相关插件
npm install --save-dev @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup
@rollup/plugin-node-resolve:用于解析 node_modules 中的第三方依赖@rollup/plugin-commonjs:用于将CommonJS模块转换为ES6模块。这样可以使得那些使用CommonJS规范编写的模块能够在Rollup中正常工作。它允许开发者将基于Node.js的模块引入到他们的Rollup项目中,并以ES6模块的形式进行处理和打包。
安装好后,在根目录下创建 rollup 的配置文件 rollup.config.js,并写入如下基本配置:
// rollup.config.js
import path from 'path'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
const config = {
// 入口文件,src/index.ts
input: path.resolve(__dirname, 'src/index.js'),
// 输出文件
output: {
file: 'dist/index.js',
format: 'cjs'
},
// 插件
plugins: [
resolve(),
commonjs(),
]
}
export default config
至此,一个基本的项目就构建好了,大致结构如图所示:
然后我们运行打包命令:
rollup --config
打包后就会在 dist 目录下生成 index.js 文件:
然后我们在命令行进行测试,可以看到我们可以成功调用 sayhelloWorld 函数:
1.1 输出多种格式
rollup 支持多种输出格式,我们可以通过配置 format 字段来指定输出的格式:
import path from 'path';
...
import pkg from './package.json'
const config = {
// 入口文件,src/index.ts
input: path.resolve(__dirname, 'src/index.ts'),
// 输出文件
outputoutput: [
// commonjs
{
file: pkg.main,
format: 'cjs'
},
// es module
{
file: pkg.module,
format: 'es'
},
// umd
{
// umd 导出文件的全局变量
name: 'rollupTest'
file: pkg.umd,
format: 'umd'
},
],
plugins: [
...
]
}
export default config
package.json 文件中加入对应的字段:
"main": "./dist/index.js",
"module": "./dist/index.esm.js",
"umd": "./dist/index.umd.js",
打包后就会在 dist 目录下生成三个不同格式的文件:
1.2 自动清空 dist
如果希望每次打包能够自动清空 dist 文件夹,可以使用 rollup-plugin-clear 插件:
npm install --save-dev rollup-plugin-clear
安装好后,在 rollup.config.js 中加入如下配置:
...
import clear from 'rollup-plugin-clear';
const config = {
...
// 插件
plugins: [
...
clear({
target: ['dist']
})
]
}
2. 使用 Babel
为了兼容低版本的的浏览器或其他环境,我们需要将高版本的JavaScript代码转换成向后兼容的JavaScript代码。但是 rollup 本身是不会进行这些处理的,例如我们在src/index.js文件中写入 ES6 的箭头函数代码。
const sayHelloWorld = () => {
console.log('hello world');
}
exports.sayHelloWorld = sayHelloWorld;
如下图,可以看到打包后的代码中并没有对箭头函数进行转换。
对此,我们需要使用 Babel 对代码进行转换。
Babel 是一个广泛使用的 JavaScript 编译器,它可以将最新的 ECMAScript 语言特性转换为向后兼容的 JavaScript 版本,以便在旧版本的浏览器或环境中运行。
首先安装 babel 相关的库:
npm install --save-dev @babel/core @babel/preset-env @babel/plugin-transform-runtime
@babel/core:Babel 的核心包,用于配置和初始化 Babel@babel/preset-env:配置目标环境,babel会根据环境来转换那些它不支持的语法
然后安装 rollup 的 babel 插件@rollup/plugin-babel
npm install --save-dev @rollup/plugin-babel
安装好后,在 rollup.config.js 中加入如下配置:
...
import { babel } from '@rollup/plugin-babel'
const config = {
...
// 插件
plugins: [
...
babel({
// 编译库
babelHelpers: 'runtime',
// 不转义依赖
exclude: 'node_modules/**',
}),
]
}
export default config
并添加babel配置文件 babel.config.js:
// babel.config.js
module.exports = {
"presets": [
[
"@babel/preset-env",
{
/* Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败 */
"modules": false
}
]
],
"plugins": [
[
// 与 babelHelpers: 'runtime' 配合使用
"@babel/plugin-transform-runtime"
]
]
}
可以看到,打包后的代码中 ES6 语法已经进行了转换:
3. 支持 Typescript
安装typescript:
npm i --save-dev typescript
安装 rollup 的 typescript 插件 rollup-plugin-typescript2:
npm i --save-dev rollup-plugin-typescript2
在 rollup.config.js 中加入如下配置:
...
import { babel } from '@rollup/plugin-babel'
const config = {
...
// 插件
plugins: [
...
typescript(),
babel({
...
// babel 默认不会转换ts
extensions: ['js', 'ts']
}),
]
}
export default config
根目录下添加 tsconfig.json 配置文件
{
"compilerOptions": {
/* 基础选项 */
"target": "esnext", /* 指定 ECMAScript 目标版本:'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "esnext", /* 输出的代码使用什么方式进行模块化: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [ /* 指定引用的标准库 */
"esnext",
"dom",
"dom.iterable",
],
"declaration": "true", /* 自动生成类型声明 */
"sourceMap": true,
"allowJs": true, /* 允许编译 js 文件 */
"removeComments": true, /* 输出不包含注释 */
/* 严格类型检查选项 */
"strict": true, /* 启用所有严格类型检查选项 */
"noImplicitAny": true, /* 检查隐含 any 类型的表达式和声明 */
"strictNullChecks": false, /* 严格空检查. */
/* 额外检查 */
"noUnusedLocals": true, /* 检查无用的变量. */
/* Module Resolution Options */
"moduleResolution": "node", /* 指定模块查找策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6) */
"baseUrl": "./", /* 查找模块的基础目录 */
"paths": {
"@/*": [
"src/*"
]
}, /* 记录 baseUrl 的模块路径查找别名 */
"types": [], /* 类型声明文件 */
},
"include": [
/* 指定编译处理的文件列表 */
"src/*.ts",
"src/types.ts"
],
}
Tips: typescript 配置可根据自己需求修改。
4. 压缩代码
当我们是在生产(production)环境中时,我们可以使用 rollup-plugin-terser 插件对代码进行压缩
npm install --save-dev rollup-plugin-terser
rollup.config.js 中加入如下配置:
...
import {terser} from 'rollup-plugin-terser';
const env = process.env.NODE_ENV;
const config = {
...
}
// 生产环境,压缩代码
if (env === 'production') {
config.plugins.push(terser({
compress: {
pure_getters: true,
unsafe: true,
unsafe_comps: true,
warnings: false
}
}))
}
export default config
我们可以使用 corss-env用于设置环境变量。并在 package.json中加入如下script:
"scripts": {
"build": "cross-env NODE_ENV=production rollup --config",
},
然后执行 npm run build 进行打包。
5. server 和 热更新
当我们在开发环境(development)时可以使用rollup-plugin-dev插件开启本地server,并使用 rollup-plugin-livereload 插件进行热更新。
npm install --save-dev rollup-plugin-dev rollup-plugin-livereload
rollup.config.js 中加入如下配置:
...
import {terser} from 'rollup-plugin-terser';
const env = process.env.NODE_ENV;
const config = {
...
}
...
// 开发环境
if (env === 'development') {
config.plugins.push(
livereload(),
dev({
port: 3000,
})
)
}
export default config
package.json 中加入如下 script:
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config -w"
},
此外我们还需要在根目录下创建一个 index.html 文件,并引入打包后的index.esm.js文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="dist/index.esm.js" type="module"></script>
</body>
</html>
然后运行 npm run dev 即可在本地进行调试啦。
- 本地调试并不一定要按本文的方式
- 在本地开启 server 也可以使用
rollup-plugin-serve插件。
有关 Rollup 的基本使用就介绍到这里了,当然还有很多方面还没涉及到。最后附上本文中测试项目中的相关代码:
pcakage.json
{
"name": "rollup-test",
"version": "1.0.0",
"description": "",
"main": "./dist/index.js",
"module": "./dist/index.esm.js",
"umd": "./dist/index.umd.js",
"scripts": {
"build": "cross-env NODE_ENV=production rollup --config",
"dev": "cross-env NODE_ENV=development rollup --config -w"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/plugin-transform-runtime": "^7.23.6",
"@babel/preset-env": "^7.23.8",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"cross-env": "^7.0.3",
"rollup": "^2.79.1",
"rollup-plugin-clear": "^2.0.7",
"rollup-plugin-dev": "^2.0.4",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.36.0",
"typescript": "^5.3.3"
}
}
rollup.config.js
import path from 'path'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import clear from 'rollup-plugin-clear';
import { babel } from '@rollup/plugin-babel'
import typescript from 'rollup-plugin-typescript2'
import {terser} from 'rollup-plugin-terser';
import dev from 'rollup-plugin-dev';
import livereload from 'rollup-plugin-livereload'
import pkg from './package.json'
const env = process.env.NODE_ENV;
const config = {
// 入口文件,src/index.ts
input: path.resolve(__dirname, 'src/index.ts'),
// 输出文件
output: [
{
file: pkg.main,
format: 'cjs'
},
{
file: pkg.module,
format: 'es'
},
{
file: pkg.umd,
format: 'umd'
},
],
// 插件
plugins: [
resolve(),
commonjs(),
typescript(),
babel({
// 编译库
babelHelpers: 'runtime',
// 不转义依赖
exclude: 'node_modules/**',
}),
clear({
targets: ['dist']
})
]
}
// 开发环境
if (env === 'development') {
config.plugins.push(
livereload(),
dev({
port: 3000,
})
)
}
// 生产环境,压缩代码
if (env === 'production') {
config.plugins.push(terser({
compress: {
pure_getters: true,
unsafe: true,
unsafe_comps: true,
warnings: false
}
}))
}
export default config