Skip to content

Rollup 构建指南 📦

简介

Rollup 是一个 JavaScript 模块打包器,专注于构建高效的库和应用程序。它原生支持 ES 模块(ESM),是构建现代 JavaScript 库的理想选择。

  • ES Module 原生支持 🚀
  • 更好的 Tree Shaking 🌳
  • 更小的包体积 📦
  • 更清晰的代码结构 🎨

项目初始化

基础配置

bash
# 创建项目
mkdir my-rollup-project
cd my-rollup-project
npm init -y

# 安装 rollup 相关依赖
npm install rollup @rollup/plugin-node-resolve @rollup/plugin-typescript -D
npm install typescript tslib @types/node -D
javascript
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';

export default defineConfig({
  input: 'src/main.ts',  // 入口文件
  output: {
    dir: 'dist',         // 输出目录
    format: 'es',        // 使用 ES 模块格式
    sourcemap: true      // 生成 sourcemap
  },
  plugins: [
    resolve(),           // 解析第三方模块
    typescript({         // TypeScript 配置
      module: 'ESNext'   // 确保 TypeScript 输出 ES 模块
    })
  ]
});
json
{
  "name": "my-rollup-project",
  "version": "1.0.0",
  "type": "module",        // 声明使用 ES 模块
  "scripts": {
    "dev": "rollup -c -w",
    "build": "rollup -c",
    "build:prod": "rollup -c --environment NODE_ENV:production"
  }
}
json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",        // 使用 ES 模块
    "moduleResolution": "bundler",  // 使用新的模块解析策略
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

ES 模块最佳实践

  1. 文件扩展名

    • 使用 .mjs 扩展名明确表示 ES 模块
    • 配置文件使用 rollup.config.mjs
    • TypeScript 文件仍使用 .ts 扩展名
  2. 导入导出语法

typescript
// ✅ 推荐的写法
export const hello = () => 'Hello';
export default class MyClass {};

// ❌ 避免的写法
module.exports = {};
const { hello } = require('./hello');
  1. 动态导入
typescript
// ✅ 使用动态导入
const module = await import('./module.js');

// ❌ 避免使用 require
const module = require('./module');

模块解析

注意事项

  1. 总是使用完整的文件扩展名
typescript
// ✅ 推荐的写法
import { hello } from './hello.js';
import { type User } from './types.ts';

// ❌ 避免的写法
import { hello } from './hello';
  1. 使用 package.json 的 exports 字段
json
{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  }
}
  1. 路径别名配置
javascript
import alias from '@rollup/plugin-alias';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default {
  plugins: [
    alias({
      entries: [
        { 
          find: '@', 
          replacement: path.resolve(__dirname, 'src') 
        }
      ]
    })
  ]
}

第三方依赖处理

依赖处理建议

  1. 优先使用提供 ES 模块的包
json
{
  "dependencies": {
    "lodash-es": "^4.17.21",  // ✅ ES 模块版本
    "lodash": "^4.17.21"      // ❌ CommonJS 版本
  }
}
  1. 外部化第三方依赖
javascript
export default {
  external: [
    /node_modules/,
    'lodash-es'
  ]
}
  1. 使用 importAssertions 处理 JSON
typescript
import config from './config.json' assert { type: 'json' };

常用插件

核心插件

基础插件

插件用途安装命令
@rollup/plugin-node-resolve解析第三方模块npm i -D @rollup/plugin-node-resolve
@rollup/plugin-commonjs转换 CommonJS 模块npm i -D @rollup/plugin-commonjs
@rollup/plugin-typescriptTypeScript 支持npm i -D @rollup/plugin-typescript
@rollup/plugin-babelBabel 转换npm i -D @rollup/plugin-babel
@rollup/plugin-alias路径别名npm i -D @rollup/plugin-alias
@rollup/plugin-jsonJSON 文件支持npm i -D @rollup/plugin-json

开发优化

javascript
import { defineConfig } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';

export default defineConfig({
  input: 'src/main.ts',
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: true
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript(),
    serve({
      open: true,
      contentBase: ['dist', 'public'],
      port: 3000
    }),
    livereload('dist')
  ]
});
javascript
import { defineConfig } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import cleanup from 'rollup-plugin-cleanup';

export default defineConfig({
  input: 'src/main.ts',
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: true
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript(),
    cleanup(),
    terser()
  ]
});

高级配置

多入口配置

多入口打包
javascript
export default {
  input: {
    main: 'src/main.ts',
    worker: 'src/worker.ts'
  },
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: true,
    entryFileNames: '[name].js'
  }
}

外部依赖

外部依赖配置

javascript
export default {
  // ... 其他配置
  external: ['lodash', 'react', 'react-dom'],
  output: {
    globals: {
      'lodash': '_',
      'react': 'React',
      'react-dom': 'ReactDOM'
    }
  }
}

代码分割

动态导入示例
typescript
// src/main.ts
async function loadModule() {
  const { hello } = await import('./hello');
  hello();
}

// rollup.config.js
export default {
  input: 'src/main.ts',
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: true,
    manualChunks(id) {
      if (id.includes('node_modules')) {
        return 'vendor';
      }
    }
  }
}

路径别名

路径别名配置

  1. 安装必要的插件:
bash
npm install -D @rollup/plugin-alias
  1. 配置 rollup.config.js:
javascript
import alias from '@rollup/plugin-alias';
import path from 'path';

export default {
  // ... 其他配置
  plugins: [
    alias({
      entries: [
        { 
          find: '@', 
          replacement: path.resolve(__dirname, 'src') 
        }
      ]
    })
  ]
}
  1. 确保 tsconfig.json 中也配置了相应的路径:
json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

环境配置

环境变量

javascript
import { defineConfig } from 'rollup';
import replace from '@rollup/plugin-replace';

export default defineConfig({
  // ... 其他配置
  plugins: [
    replace({
      preventAssignment: true,
      values: {
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
        'process.env.API_URL': JSON.stringify(process.env.API_URL)
      }
    })
  ]
});
bash
API_URL=http://localhost:3000
bash
API_URL=https://api.example.com

性能优化

优化建议

  1. Tree Shaking

    • 使用 ES 模块语法
    • 避免副作用
    • 使用 "sideEffects": false 或指定有副作用的文件
  2. 代码分割

    • 使用动态导入
    • 配置 manualChunks
    • 提取公共依赖
  3. 构建优化

    • 使用 terser 压缩代码
    • 配置 external 减小包体积
    • 使用 sourcemap 优化调试

常见问题

常见问题解决

  1. 模块解析问题

    javascript
    // 检查 plugin-node-resolve 配置
    resolve({
      extensions: ['.js', '.ts'],
      preferBuiltins: true
    })
  2. CommonJS 模块问题

    javascript
    // 配置 plugin-commonjs
    commonjs({
      transformMixedEsModules: true,
      include: 'node_modules/**'
    })
  3. TypeScript 路径别名

    javascript
    // 同时配置 rollup 和 typescript
    typescript({
      tsconfig: './tsconfig.json'
    })

部署配置

部署检查清单

  1. 环境变量配置
  2. 代码压缩
  3. Source Map 处理
  4. 静态资源处理
  5. 缓存策略
  6. 错误处理

开发工具

  • rollup-plugin-visualizer - 包大小分析
  • rollup-plugin-serve - 开发服务器
  • rollup-plugin-livereload - 热重载
  • @rollup/plugin-terser - 代码压缩
  • rollup-plugin-cleanup - 代码清理

Node.js 后端项目注意事项

当使用 Rollup 构建 Node.js 后端项目(如 Express 应用)时,需要特别注意:

  1. 安装必要的插件:
bash
# 安装 CommonJS 支持插件
npm install -D @rollup/plugin-commonjs
  1. 更新 rollup.config.mjs:
javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';

export default defineConfig({
  input: 'src/main.ts',
  output: {
    dir: 'dist',
    format: 'cjs',        // Node.js 环境使用 CommonJS 格式
    sourcemap: true
  },
  plugins: [
    resolve({
      preferBuiltins: true  // 优先使用 Node.js 内置模块
    }),
    commonjs({
      extensions: ['.js', '.ts']  // 需要转换的文件扩展名
    }),
    typescript({
      module: 'ESNext'
    })
  ],
  external: [
    'express',   // 将 express 标记为外部依赖
    /node_modules/  // 所有 node_modules 中的包都作为外部依赖
  ]
});
  1. package.json 配置:
json
{
  "type": "module",        // 源码使用 ES 模块
  "main": "dist/main.js",  // 输出 CommonJS 格式
  "dependencies": {
    "express": "^4.18.2"
  }
}
  1. 源码中的导入方式:
typescript
// ✅ 使用 ES 模块语法导入
import express from 'express';
import type { Request, Response } from 'express';

const app = express();

// 其他代码...

为什么需要这样配置?

  1. Node.js 生态中仍然有大量 CommonJS 模块(如 Express)
  2. 虽然我们的源码使用 ES 模块语法,但构建后的代码需要兼容 Node.js 环境
  3. 使用 format: 'cjs' 确保输出的代码可以在 Node.js 中正常运行
  4. 使用 external 可以避免打包 node_modules 中的依赖,减小包体积

常见问题

  1. "default" is not exported

    • 这通常是因为 CommonJS 模块没有默认导出
    • 使用 @rollup/plugin-commonjs 可以解决
  2. Cannot use import statement outside a module

    • 确保 package.json 中设置了 "type": "module"
    • 或者使用 .mjs 扩展名
  3. 模块解析问题

    • 使用 preferBuiltins: true 优先使用 Node.js 内置模块
    • 正确配置 external 选项避免打包不必要的依赖