rollup的基本使用

1,029 阅读3分钟

来自掘金小册 深入浅出 Vite 的学习实践与总结

概述

  1. 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();

上面打包过程如下:

  1. 使用rollup.rollup(),传入输入配置,生成bundle
  2. 使用bundle调用generate()write()分别生成产物与写入磁盘,入参都是输出配置
  3. 通过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('构建结束');
  }
});