# rollup使用教程
最近要开发一个sdk,用webpack感觉有点重了,而gulp对于模块无能为力,发现很多npm库都是用rollup开发的。rollup对于纯js的编译很不错,相对于webpack,速度更快,打包后的代码体积更小,会通过tree-shake去除未使用的import。 详细文档见rollup中文网。
# 安装
npm install rollup rollup-copy-plugin rollup-plugin-babel rollup-plugin-commonjs rollup-plugin-copy rollup-plugin-json rollup-plugin-node-resolve rollup-plugin-replace rollup-plugin-typescript2 rollup-plugin-uglify
后面的是相关插件
# 使用
有两种使用方法:
- 直接使用配置文件
新建一个rollup.config.js的文件
// rollup.config.js
export default {
// 核心选项
input, // 必须
external,
plugins,
// 额外选项
onwarn,
// danger zone
acorn,
context,
moduleContext,
legacy
output: { // 必须 (如果要输出多个,可以是一个数组)
// 核心选项
file, // 必须
format, // 必须
name,
globals,
// 额外选项
paths,
banner,
footer,
intro,
outro,
sourcemap,
sourcemapFile,
interop,
// 高危选项
exports,
amd,
indent
strict
},
};
// 运行命令
rollup -c
命令行参数:
-i, --input 要打包的文件(必须)
-o, --output.file 输出的文件 (如果没有这个参数,则直接输出到控制台)
-f, --output.format [es] 输出的文件类型 (amd, cjs, es, iife, umd)
-e, --external 将模块ID的逗号分隔列表排除
-g, --globals 以`module ID:Global` 键值对的形式,用逗号分隔开
任何定义在这里模块ID定义添加到外部依赖
-n, --name 生成UMD模块的名字
-m, --sourcemap 生成 sourcemap (`-m inline` for inline map)
--amd.id AMD模块的ID,默认是个匿名函数
--amd.define 使用Function来代替`define`
--no-strict 在生成的包中省略`"use strict";`
--no-conflict 对于UMD模块来说,给全局变量生成一个无冲突的方法
--intro 在打包好的文件的块的内部(wrapper内部)的最顶部插入一段内容
--outro 在打包好的文件的块的内部(wrapper内部)的最底部插入一段内容
--banner 在打包好的文件的块的外部(wrapper外部)的最顶部插入一段内容
--footer 在打包好的文件的块的外部(wrapper外部)的最底部插入一段内容
--interop 包含公共的模块(这个选项是默认添加的)
- 使用api调用
有两个相关api:
- rollup.rollup
执行rollup编译
const rollup = require('rollup'); // see below for details on the options const inputOptions = {...}; const outputOptions = {...}; async function build() { // create a bundle const bundle = await rollup.rollup(inputOptions); console.log(bundle.imports); // an array of external dependencies console.log(bundle.exports); // an array of names exported by the entry point console.log(bundle.modules); // an array of module objects // generate code and a sourcemap const { code, map } = await bundle.generate(outputOptions); // or write the bundle to disk await bundle.write(outputOptions); } build();
- rollup.watch 监听
const rollup = require('rollup');
const watchOptions = {...};
const watcher = rollup.watch(watchOptions);
watcher.on('event', event => {
// event.code 会是下面其中一个:
// START — 监听器正在启动(重启)
// BUNDLE_START — 构建单个文件束
// BUNDLE_END — 完成文件束构建
// END — 完成所有文件束构建
// ERROR — 构建时遇到错误
// FATAL — 遇到无可修复的错误
});
// 停止监听
watcher.close();
通过这两个api,就可以编写js文件,通过npm script运行,也可以与gulp等工具联合使用。
# 相关插件
- rollup-plugin-node-resolve 告诉 Rollup 如何查找外部模块
- rollup-plugin-commonjs 将CommonJS模块转换为 ES2015 供 Rollup 处理。
- rollup-plugin-babel
babel编译
//配置项 export default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' }, plugins: [ resolve(), babel({ exclude: 'node_modules/**' // 只编译我们的源代码 }) ] };
//.babelrc文件 { "presets": [ ["@babel/preset-env", { "modules": false, }] ] }
- rollup-plugin-copy 复制文件
- rollup-plugin-json 支持json文件
- rollup-plugin-replace
获取环境变量需要转换
export default { // ... plugins: [ replace({ ENVIRONMENT: JSON.stringify('production') }) ] };
- rollup-plugin-typescript2 typescript支持
- rollup-plugin-uglify js代码uglify
# 我的配置
我的sdk中有一个入口文件编译为核心js,还有多个lib文件需要编译为独立文件。因此配置项的方式不太适用,使用js文件通过api编译。
// rollup.config.js
const fs = require('fs');
const path = require('path');
const rollup = require('rollup');
const commonjs = require('rollup-plugin-commonjs');
const replace = require('rollup-plugin-replace');
const resolve = require('rollup-plugin-node-resolve');
const babel = require('rollup-plugin-babel');
const uglify = require('rollup-plugin-uglify').uglify;
const json = require('rollup-plugin-json');
// const copy = require('rollup-plugin-copy');
const chalk = require('chalk');
const typescript = require('rollup-plugin-typescript2');
const env = process.env.NODE_ENV;
const inputRoot = 'src/libs';
const outputRoot = 'dist/libs';
init();
function init () {
// 检查目录是否存在,不存在则创建。
checkDirSync(outputRoot);
// 然后继续执行
walk();
}
// 遍历目录
function walk () {
console.log(chalk.blue('遍历libs目录进行编译'));
fs.readdir(inputRoot, function (err, files) {
if (err) throw (err);
files.map(file => {
const url = path.resolve(inputRoot, file);
fs.stat(url, function (err2, stat) {
if (err2) throw err2;
// 对每个lib目录进行编译
if (stat.isDirectory()) {
compile(file, 'api', 'front');
compile(file, 'api', 'back');
compile(file, 'desc');
}
});
});
});
}
// 编译lib
function compile (file, type, end) {
checkDirSync('dist');
const inputOptions = {
input: type === 'api' ? `${inputRoot}/${file}/${end}.ts` : `${inputRoot}/${file}/desc.ts`, // 必须
plugins: [
typescript({
tsconfig: 'tsconfig.json'
}),
resolve(),
babel({
exclude: 'node_modules/**'
}),
replace({
'process.env.NODE_ENV': JSON.stringify(env)
}),
commonjs(),
json()
],
};
const outputOptions = {
file: type === 'api' ? `${outputRoot}/jl-lib-${file}-${end}.js` : `${outputRoot}/jl-lib-${file}.desc.js`,
name: type === 'api' ? `jl-lib-${file}-${end}` : `jl-lib-${file}-desc`, // umd或iife模式下,若入口文件含 export,必须加上该属性
format: 'umd',
sourcemap: true,
};
const watchOptions = {
...inputOptions,
output: [outputOptions],
watch: {
chokidar: true,
}
};
if (env === 'production') {
// 生产环境
inputOptions.plugins.push(
uglify({
compress: {
drop_console: true,
}
}),
);
}
// 执行rollup
rollup
.rollup(inputOptions)
.then(function (bundle) {
bundle.write(outputOptions);
});
// 监听
// event.code 会是下面其中一个:
// START — 监听器正在启动(重启)
// BUNDLE_START — 构建单个文件束
// BUNDLE_END — 完成文件束构建
// END — 完成所有文件束构建
// ERROR — 构建时遇到错误
// FATAL — 遇到无可修复的错误
const watcher = rollup.watch(watchOptions);
if (env === 'production') {
watcher.on('event', event => {
if (event.code === 'END') {
console.log(chalk.greenBright(type === 'api' ? `创建dist/libs/jl-lib-${file}-${end}.js成功` : `创建dist/libs/jl-lib-${file}.desc.js成功`));
watcher.close();
}
});
} else {
console.log(chalk.yellow(type === 'api' ? `监听dist/libs/jl-lib-${file}-${end}.js` : `监听dist/libs/jl-lib-${file}.desc.js`));
checkDirSync('minapp_demo/jl-sdk/libs');
checkDirSync('admin_demo/jl-sdk/libs');
watcher.on('event', event => {
if (event.code === 'END') {
console.log(chalk.greenBright(type === 'api' ? `创建dist/libs/jl-lib-${file}-${end}.js成功` : `创建dist/libs/jl-lib-${file}.desc.js成功`));
// 复制
console.log(chalk.gray(type === 'api' ? `复制dist/libs/jl-lib-${file}-${end}.js到demo` : `复制dist/libs/jl-lib-${file}.desc.js到demo`));
copy(
path.join('dist/libs', type === 'api' ? `jl-lib-${file}-${end}.js` : `jl-lib-${file}.desc.js`),
[
path.join('minapp_demo/jl-sdk/libs/', type === 'api' ? `jl-lib-${file}-${end}.js` : `jl-lib-${file}.desc.js`),
path.join('admin_demo/jl-sdk/libs/', type === 'api' ? `jl-lib-${file}-${end}.js` : `jl-lib-${file}.desc.js`),
path.join('../newFashion-sdk/libs/jl-sdk/', type === 'api' ? `jl-lib-${file}-${end}.js` : `jl-lib-${file}.desc.js`),
]
);
}
});
}
}
function checkDirSync (src) {
try {
fs.readdirSync(src);
} catch (err) {
if (err) {
console.log(`${src}目录不存在,创建目录`);
fs.mkdirSync(src);
}
}
}
function copy (src, dsts) {
dsts.map(dst => {
fs.writeFileSync(dst, fs.readFileSync(src));
});
}
package.json中相关脚本如下:
{
"scripts": {
"build:core": "cross-env NODE_ENV=production node rollup.config.core.js",
"build:libs": "cross-env NODE_ENV=production node rollup.config.libs.js",
"build": "npm run clean:dist && npm run build:core && npm run build:libs",
"dev:core": "cross-env NODE_ENV=development node --inspect rollup.config.core.js",
"dev:libs": "cross-env NODE_ENV=development node rollup.config.libs.js",
"dev": "npm run clean:dist && concurrently 'npm:dev:core' 'npm:dev:libs'",
"start": "npm run dev",
"clean:dist": "rimraf dist/**/*",
}
}