72. Rollup 教程

1. Rollup 简介

Rollup 是一个 JavaScript 模块打包器,专注于创建更小、更高效的库和应用程序。与 Webpack 不同,Rollup 更注重代码的精简和性能,特别适合构建 JavaScript 库。

1.1 Rollup 的核心特性

  • Tree Shaking:自动移除未使用的代码,减小打包体积
  • ES 模块优先:原生支持 ES 模块,输出更简洁的代码
  • 多种输出格式:支持输出 ES、CommonJS、UMD、AMD 等多种模块格式
  • 插件生态系统:通过插件扩展功能
  • 代码分割:支持动态导入和代码分割

1.2 Rollup 与 Webpack 的比较

特性 Rollup Webpack
主要用途 库打包 应用打包
Tree Shaking 更高效 支持但相对复杂
配置复杂度 简单 复杂
输出体积 更小 相对较大
代码分割 支持 更强大
热模块替换 有限支持 强大支持

2. 安装与配置

2.1 安装 Rollup

首先,在项目中安装 Rollup 和相关插件:

# 使用 npm
npm install --save-dev rollup

# 使用 yarn
yarn add --dev rollup

# 使用 pnpm
pnpm add --save-dev rollup

2.2 基本配置文件

在项目根目录创建 rollup.config.js 文件:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  }
};

2.3 配置 package.json 脚本

package.json 文件中添加构建脚本:

{
  "scripts": {
    "build": "rollup -c",
    "watch": "rollup -c -w"
  }
}

3. 基本使用

3.1 处理 JavaScript 模块

创建 src/index.js 文件:

// 导入模块
import { add } from './utils.js';

// 导出模块
export const calculator = {
  add,
  multiply: (a, b) => a * b
};

创建 src/utils.js 文件:

// 导出模块
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// 这个函数不会被打包,因为没有被使用
export const divide = (a, b) => a / b;

3.2 多种输出格式

修改 rollup.config.js 文件,支持多种输出格式:

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.esm.js',
      format: 'esm'
    }
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    }
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.umd.js',
      format: 'umd',
      name: 'MyLibrary'
    }
  }
];

3.3 使用插件

安装常用插件:

npm install --save-dev @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel

更新 rollup.config.js 文件:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve(),
    commonjs(),
    babel({
      babelHelpers: 'bundled',
      exclude: 'node_modules/**'
    })
  ]
};

4. 高级特性

4.1 Tree Shaking 优化

Rollup 的 Tree Shaking 功能会自动分析代码依赖,只打包被使用的代码。例如:

// src/index.js
import { add } from './utils.js';

// 只使用了 add 函数,subtract 和 divide 不会被打包
console.log(add(1, 2));

4.2 代码分割

使用动态导入实现代码分割:

// src/index.js
export async function loadHeavyModule() {
  const { default: heavyModule } = await import('./heavy-module.js');
  return heavyModule;
}

配置代码分割:

export default {
  input: 'src/index.js',
  output: {
    dir: 'dist',
    format: 'esm'
  }
};

4.3 外部依赖

配置外部依赖,避免将某些库打包进最终文件:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  external: ['react', 'lodash']
};

4.4 环境变量

使用 @rollup/plugin-replace 插件处理环境变量:

npm install --save-dev @rollup/plugin-replace
import replace from '@rollup/plugin-replace';

export default {
  // ... 其他配置
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
};

5. 最佳实践

5.1 目录结构

推荐的目录结构:

├── dist/
├── src/
│   ├── index.js         # 入口文件
│   ├── utils.js         # 工具函数
│   └── components/      # 组件
├── rollup.config.js     # Rollup 配置
└── package.json         # 项目配置

5.2 配置分离

对于复杂项目,可将配置分离为多个文件:

  • rollup.config.js:公共配置
  • rollup.config.dev.js:开发环境配置
  • rollup.config.prod.js:生产环境配置

5.3 性能优化

  • 使用 ES 模块:优先使用 ES 模块语法,获得更好的 Tree Shaking 效果
  • 合理配置外部依赖:避免打包大型库
  • 使用缓存:启用 Rollup 的缓存功能
  • 并行构建:对于多入口项目,考虑使用并行构建

5.4 代码质量

  • 使用 ESLint:确保代码质量
  • 使用 Prettier:保持代码风格一致
  • 添加类型检查:对于 TypeScript 项目,使用 TypeScript 进行类型检查

6. 插件系统

6.1 常用插件

插件名称 功能 安装命令
@rollup/plugin-node-resolve 解析 node_modules 中的模块 npm install --save-dev @rollup/plugin-node-resolve
@rollup/plugin-commonjs 将 CommonJS 模块转换为 ES 模块 npm install --save-dev @rollup/plugin-commonjs
@rollup/plugin-babel 使用 Babel 转换代码 npm install --save-dev @rollup/plugin-babel
@rollup/plugin-terser 压缩 JavaScript 代码 npm install --save-dev @rollup/plugin-terser
@rollup/plugin-typescript 支持 TypeScript npm install --save-dev @rollup/plugin-typescript
rollup-plugin-postcss 处理 CSS 文件 npm install --save-dev rollup-plugin-postcss
rollup-plugin-json 导入 JSON 文件 npm install --save-dev @rollup/plugin-json

6.2 自定义插件

创建简单的自定义插件:

// rollup.config.js
function myPlugin() {
  return {
    name: 'my-plugin',
    transform(code, id) {
      // 转换代码
      return code.replace(/console\.log\(['"](.+)['"]\)/g, '// console.log("$1")');
    }
  };
}

export default {
  // ... 其他配置
  plugins: [
    myPlugin()
  ]
};

7. 示例项目

7.1 基本库项目

7.1.1 项目结构

├── dist/
├── src/
│   ├── index.js
│   ├── math.js
│   └── string.js
├── rollup.config.js
└── package.json

7.1.2 源代码

// src/math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;

// src/string.js
export const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
export const truncate = (str, length) => str.length > length ? str.slice(0, length) + '...' : str;

// src/index.js
import * as math from './math.js';
import * as string from './string.js';

export default {
  ...math,
  ...string
};

export { math, string };

7.1.3 Rollup 配置

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.esm.js',
      format: 'esm'
    },
    plugins: [resolve(), commonjs()]
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    },
    plugins: [resolve(), commonjs()]
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.umd.js',
      format: 'umd',
      name: 'MyUtils'
    },
    plugins: [resolve(), commonjs(), terser()]
  }
];

7.1.4 构建命令

npm run build

7.2 带有代码分割的项目

7.2.1 源代码

// src/index.js
export async function loadMathModule() {
  const { add, multiply } = await import('./math.js');
  return { add, multiply };
}

export async function loadStringModule() {
  const { capitalize } = await import('./string.js');
  return { capitalize };
}

7.2.2 Rollup 配置

export default {
  input: 'src/index.js',
  output: {
    dir: 'dist',
    format: 'esm'
  }
};

8. 常见问题与解决方案

8.1 Tree Shaking 不生效

问题:未使用的代码没有被移除

解决方案

  • 确保使用 ES 模块语法
  • 检查插件配置是否正确
  • 避免使用副作用较大的代码

8.2 外部依赖处理

问题:外部依赖被打包进最终文件

解决方案

  • external 选项中正确配置外部依赖
  • 对于 UMD 格式,使用 globals 选项指定全局变量

8.3 动态导入问题

问题:动态导入不工作

解决方案

  • 确保输出格式支持动态导入(如 esm、amd)
  • 使用 output.dir 而不是 output.file

8.4 兼容性问题

问题:打包后的代码在某些环境下不工作

解决方案

  • 使用 Babel 转换代码
  • 选择合适的输出格式
  • 添加必要的 polyfill

9. 总结

Rollup 是一个专注于代码精简和性能的 JavaScript 模块打包器,特别适合构建 JavaScript 库。通过本教程的学习,你应该已经掌握了:

  • Rollup 的核心概念和基本使用方法
  • 如何配置 Rollup 处理不同类型的文件
  • 如何使用 Rollup 的高级特性如 Tree Shaking 和代码分割
  • Rollup 的最佳实践和性能优化技巧
  • 如何使用和开发 Rollup 插件

Rollup 的设计理念是"做一件事并做好它",它在库打包方面的表现非常出色。对于需要构建小型、高效的 JavaScript 库的开发者来说,Rollup 是一个理想的选择。

10. 扩展阅读

« 上一篇 71. Webpack 教程 下一篇 » 73. Parcel 教程