来自掘金小册 深入浅出 Vite 的学习实践与总结
概述
- rollup 具有天然的 Tree Shaking 功能
Rollup 可以在编译阶段分析出依赖关系,对 AST 语法树中没有使用到的节点进行删除,从而实现 Tree Shaking。
常用命令
| 命令 | 说明 |
|---|---|
| rollup -c | 使用配置文件进行打包 |
基于配置文件进行使用
基本使用:
// rollup.config.js
// 加载第三方包
import resolve from '@rollup/plugin-node-resolve';
// 将 commonjs 转换为 ESM
import resolveCommonjs from '@rollup/plugin-commonjs';
// 代码压缩
import { terser } from 'rollup-plugin-terser';
const option = {
// 配置入口文件,可单个,可多个
input: ['src/index.js', 'src/util.js'],
// 输出配置,可单个,也可以多个
output: [
{
// 产物输出格式,包括`amd`、`cjs`、`es`、`iife`、`umd`、`system`
format: 'esm',
// 输出目录(每次编译不会自动清理已有目录)
dir: 'dist/es',
// 以下三个配置项都可以使用这些占位符:
// 1. [name]: 文件名称,不包括".后缀名"
// 2. [hash]: 根据文件名和文件内容生成的 hash 值
// 3. [format]: 产物模块格式,如 es、cjs
// 4. [extname]: 产物后缀名(带`.`)
entryFileNames: '[name].js', // 入口文件
// 拆分的块。例如使用动态导入,就会被单独打包成一个文件
chunkFileNames: 'chunk-[name].js',
// 静态资源
assetFileNames: 'assets/[name]-[hash][extname]',
// 生成 sourcemap 文件
sourcemap: true,
/*
这里可以使用的插件有限制,
只有使用 Output 阶段相关
钩子的插件才能使用
*/
plugins: [terser()],
},
{
format: 'commonjs',
dir: 'dist/cjs',
},
],
// 使用插件
plugins: [resolve(), resolveCommonjs()],
};
export default option;
如果是多个入口,并且配置不同,则可以导出一个数组:
// rollup.config.js
const option1 = {
// ...
};
const option2 = {
// ...
};
export default [option1, option2];
排除外部依赖以及打包成 IIFE 、UMD 格式:
// rollup.config.js
const option = {
input: ['src/index.js'],
output: [
{
// 产物格式为 IIFE
format: 'iife',
dir: 'dist/iife',
// 打包后的全局变量名称
name: 'MyBundle',
// 全局模块
globals: {
// 告诉 rollup,全局模块 Vue 等同于 vue
vue: 'Vue',
},
},
],
// 排除 vue 依赖,不进行打包
external: ['vue'],
};
export default option;
下面进行全局模块解释,下面是一个入口文件,有这么些代码:
// index.js
import { ref } from 'vue';
console.log(ref);
然后使用上面的配置打包后的代码:
// index.js(打包后)
// 传递全局模块 vue 时,使用的名称是 Vue
// 我们自己写的代码中,使用的模块名称是 vue
// 这样就能确保正确使用外部模块
var MyBundle = (function (exports, vue) {
console.log(vue.ref);
})({}, Vue);
JavaScript API 方式调用
rollup 的 JavaScript API 主要是:rollup.rollup()、 rollup.watch()。
rollup.rollup()
使用rollup.rollup()进行一次性打包,入参就是RollupOptions,跟配置文件一样:
// build.js
/**
* 输入配置
* @type { import('rollup').RollupOptions }
*/
const inputOptions = {
input: './src/index.js',
plugins: [resolve(), commonjs()],
};
/**
* 输出配置
* @type { import('rollup').RollupOptions['output'] }
*/
const outputOptions = [
{
format: 'esm',
dir: 'dist/es',
entryFileNames: '[name].js',
chunkFileNames: 'chunk-[name].js',
sourcemap: true,
assetFileNames: 'assets/[name]-[hash][extname]',
},
{
format: 'commonjs',
dir: 'dist/cjs',
},
];
async function build() {
let bundle;
let buildFailed = false;
try {
// 生成 bundle 对象
bundle = await rollup.rollup(inputOptions);
for (const outputOption of outputOptions) {
// 生成打包产物
await bundle.generate(outputOption);
// 将产物写入磁盘
await bundle.write(outputOption);
}
} catch (error) {
buildFailed = true;
console.log('报错', error);
}
if (bundle) {
// 打包成功,结束打包
bundle.close();
}
process.exit(buildFailed ? 1 : 0);
}
build();
上面打包过程如下:
- 使用
rollup.rollup(),传入输入配置,生成bundle - 使用
bundle调用generate()和write()分别生成产物与写入磁盘,入参都是输出配置 - 通过
bundle.close()结束打包
rollup.watch()
使用rollup.watch()监听文件,进行持续编译构建,监听过程中还有一些事件会被触发:
// watch.js
// 入参跟配置文件一样,
// 就是多了个 watch 配置
const watcher = rollup.watch({
input: './src/index.js',
plugins: [resolve(), commonjs()],
output: [
{
format: 'esm',
dir: 'dist/es',
entryFileNames: '[name].js',
chunkFileNames: 'chunk-[name].js',
sourcemap: true,
assetFileNames: 'assets/[name]-[hash][extname]',
},
{
format: 'commonjs',
dir: 'dist/cjs',
},
],
// 配置监听的范围
watch: {
include: ['src/**'],
},
});
// 监听 watcher 的各种事件
watcher.on('restart', () => {
console.log('重新构建...');
});
watcher.on('change', (id) => {
console.log('发生变动的模块id: ', id);
});
watcher.on('event', (e) => {
if (e.code === 'BUNDLE_END') {
console.log('构建结束');
}
});