Skip to content

rollup.js

介绍

​ Rollup 是JavaScript模块打包工具,与 webpack 常用于打包页面应用不同,Rollup 更专注于类库的打包,支持打包为IIFEESMCJSAMDUMD等模块;Rollup 使用的是 ES6 版本 Javascript 中的模块标准,更好的tree shaking支持;

安装

npm i -g rollup

配置文件

​ Rollup 支持配置文件来扩展更强的能力,命令行运行rollup --config 配置文件(--config 可简写为-c,配置文件路径可省略,默认查找顺序为:rollup.config.mjs => rollup.config.cjs => rollup.config.js)来执行配置文件;

​ 默认配置文件rollup.config.jsESM默认模块,最基础的配置文件:

export default {
  // 入口文件
  input: ["./src/index.js"],
  output: {
    // 输出指定格式模块
    format: "umd",
  },
};

​ 运行命令支持传递自定义参数用作配置文件内执行相关的操作;

​ 配置项完整列表:

// rollup.config.js

export default {
  // 可以是一个数组(用于多个输入的情况)
  // 核心的输入选项
  external,
  input, // 必要项
  plugins,

  // 高级输入选项
  cache,
  onwarn,
  preserveEntrySignatures,
  strictDeprecations,

  // 危险区
  acorn,
  acornInjectPlugins,
  context,
  moduleContext,
  preserveSymlinks,
  shimMissingExports,
  treeshake,

  // 实验性
  experimentalCacheExpiry,
  perf,

  output: {
    // 必要项 (可以是一个数组,用于多输出的情况)
    // 核心的输出选项
    dir,
    file,
    format, // 必要项
    globals,
    name,
    plugins,

    // 高级输出选项
    assetFileNames,
    banner,
    chunkFileNames,
    compact,
    entryFileNames,
    extend,
    footer,
    hoistTransitiveImports,
    inlineDynamicImports,
    interop,
    intro,
    manualChunks,
    minifyInternalExports,
    outro,
    paths,
    preserveModules,
    sourcemap,
    sourcemapExcludeSources,
    sourcemapFile,
    sourcemapPathTransform,

    // 危险区
    amd,
    esModule,
    exports,
    externalLiveBindings,
    freeze,
    indent,
    namespaceToStringTag,
    noConflict,
    preferConst,
    strict,
    systemNullSetters,
  },

  watch:
    {
      buildDelay,
      chokidar,
      clearScreen,
      skipWrite,
      exclude,
      include,
    } | false,
};

命令行参数集

​ 注意:命令行参数会覆盖配置文件相应的配置;

-c, --config <filename>     使用配置文件(如果使用参数但是值没有
                              指定, 默认就是 rollup.config.js)
-d, --dir <dirname>         构建块的目录(如果不存在,就打印到标准输出)
-e, --external <ids>        逗号分隔列出排除的模块 ID
-f, --format <format>       输出类型 (amd, cjs, es, iife, umd, system)
-g, --globals <pairs>       逗号分隔列出 `moduleID:Global`
-h, --help                  显示帮助信息
-i, --input <filename>      输入 (替代 <entry file>)
-m, --sourcemap             生成 sourcemap (`-m inline` 生成行内 map)
-n, --name <name>           UMD 导出的名字
-o, --file <output>         单个的输出文件(如果不存在,就打印到标准输出)
-p, --plugin <plugin>       使用指定的插件(可能重复)
-v, --version               显示版本号
-w, --watch                 监听 bundle 中的文件并在文件改变时重新构建
--amd.id <id>               AMD 模块 ID(默认是匿名的)
--amd.define <name>         代替 `define` 使用的功能
--assetFileNames <pattern>  构建的资源命名模式
--banner <text>             插入 bundle 顶部(包装器之外)的代码
--chunkFileNames <pattern>  次要构建块的命名模式
--compact                   压缩包装器代码
--context <variable>        指定顶层的 `this`
--entryFileNames <pattern>  入口构建块的命名模式
--environment <values>      设置传递到配置文件 (看示例)
--no-esModule               不增加 __esModule 属性
--exports <mode>            指定导出的模式 (auto, default, named, none)
--extend                    通过 --name 定义,拓展全局变量
--no-externalLiveBindings   不生成实施绑定的代码
--footer <text>             插入到 bundle 末尾的代码(包装器外部)
--no-freeze                 不冻结命名空间对象
--no-hoistTransitiveImports 不提升传递性的导入到入口构建块
--no-indent                 结果中不进行缩进
--no-interop                不包含互操作块
--inlineDynamicImports      使用动态导入时创建单个 bundle
--intro <text>              在 bundle 顶部插入代码(包装器内部)
--minifyInternalExports     强制或者禁用内部导出的压缩
--namespaceToStringTag      为命名空间创建正确的 `.toString` 方法
--noConflict                为 UMD 全局变量生成 noConflict 方法
--outro <text>              在 bundle 的末尾插入代码(包装器内部)
--preferConst               使用 `const` 代替 `var` 进行导出
--no-preserveEntrySignatures 避免表面的构建块作为入口
--preserveModules           保留模块结构
--preserveSymlinks          解析文件时不要遵循符号链接
--shimMissingExports        给丢失的导出创建填充变量
--silent                    不打印警告
--sourcemapExcludeSources   source map 中不包含源码
--sourcemapFile <file>      source map 中指定 bundle 的路径
--no-stdin                  不从标准输入中读取 "-"
--no-strict                 在生成的模块中不使用 `"use strict";`
--strictDeprecations        不推荐使用的特性抛出错误
--systemNullSetters         用 `null` 替换空的 SystemJS setter
--no-treeshake              禁用 tree-shaking 优化
--no-treeshake.annotations  忽略纯的调用注释
--no-treeshake.moduleSideEffects 假设模块没有副作用
--no-treeshake.propertyReadSideEffects 忽略属性访问的副作用
--no-treeshake.tryCatchDeoptimization 不关闭 try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects 假设未知的全局变量不抛出
--waitForBundleInput        等待 bundle 的输入文件
--watch.buildDelay <number> 监听重新构建的延时
--no-watch.clearScreen      重新构建时不进行清屏
--watch.skipWrite           监听时不写入文件到磁盘
--watch.exclude <files>     监听时排除的文件
--watch.include <files>     限制监听指定的文件

插件

JSON

# 解决json文件
npm install --save-dev @rollup/plugin-json

应用:

// rollup.config.js
import json from "@rollup/plugin-json";

export default {
  input: "src/main.js",
  output: {
    file: "bundle.js",
    format: "cjs",
  },
  plugins: [json()],
};

resolve

# 解决引入的npm包未被打包的问题
npm install --save-dev @rollup/plugin-node-resolve

应用:同上

示例:

import deepmerge from "deepmerge";
// 默认情况下,rollup不会打包node_modules中的包,需要使用resolve插件,
// 否则会报错(报错使用路径简写,如./utils/index.js简写为./utils,是会报错的)
// 所以大部分情况下都需要使用resolve插件

babel

# 引入babel解决es6语法兼容
npm install --save-dev @rollup/plugin-babel @babel/preset-env @babel/core

应用:

// rollup.config.js
import babel from "@rollup/plugin-babel";

export default {
  input: "src/main.js",
  output: {
    file: "bundle.js",
    format: "cjs",
  },
  plugins: [babel({ exclude: "node_modules/**" })],
};

.babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ]
}

​ 注意先将 modules 设置为 false,否则 Babel 将在 Rollup 有机会执行其操作之前将模块转换为 CommonJS ,从而导致其失败;

commonjs

# 将commonjs模块的包转成es2015,而被rollup识别并处理,
# 需要注意的是,搭配babel使用时,需要将babel的modules设置为false
# 并且需要将commonjs放在babel之前
npm install --save-dev @rollup/plugin-commonjs

应用:同上

terser

# 代码压缩
npm install --save-dev rollup-plugin-terser

应用:同上

ts

# 识别ts
npm install --save-dev @rollup/plugin-typescript

应用:同上

注意项:需要安装 tslib、typescript

删除旧目录

# 删除旧的打包目录,避免打包后的文件夹中存在旧的文件
npm install --save-dev rollup-plugin-delete

应用:

// rollup.config.js
import deletePlugin from "rollup-plugin-delete";

export default {
  input: "src/main.js",
  output: {
    file: "bundle.js",
    format: "cjs",
  },
  plugins: [
    deletePlugin({
      targets: "dist/*",
    }),
  ],
};

css

# 识别css
npm install rollup-plugin-postcss --save-dev

应用:同上

开发服务器

# 可以启动一个开发服务器,注意rollup运行指令需指明--watch参数来实现监听
npm install rollup-plugin-serve --save-dev

应用:

// rollup.config.js
import json from "@rollup/plugin-json";
import serve from "rollup-plugin-serve";

export default {
  input: "src/main.js",
  output: {
    file: "bundle.js",
    format: "cjs",
  },
  plugins: [json(), serve({ open: true, port: 8080 })],
};

​ 注意:需要在根目录下新建 index.html 并引入入口 js 文件,rollup-plugin-dev也可以实现开发服务器(提供更多的功能)

热更新

# 启动开发服务器后,实现hmr功能
npm install rollup-plugin-livereload --save-dev

应用:同上

代码分割

UMDIIFE模块不支持代码分割,动态引入其它模块(import 回调),需指定打包输出目录,实现代码分割,如

// src/foo.js
export default { a: 1 }

// src/main.js
export default function () {
  import('./foo.js').then(({ default: foo }) => console.log(foo));
}

​ 配置文件:

// rollup.config.js
import json from "@rollup/plugin-json";

export default {
  input: "src/main.js",
  output: {
    dir: "dist",
    format: "cjs",
  },
  plugins: [json()],
};

​ 多入口文件打包也可实现代码分割;

外部引入

​ 假设你正在构建一个类似 React 和 Lodash 这样具有前置依赖的库,如果你按照上述的方式设置外部引入(external),rollup 将会打包 所有 的引入项:

import answer from 'the-answer';
import _ from 'lodash';

​ 你可以微调哪些引入需要被打包,哪些引入需要设置为外部引入。在这个例子中,我们可以将 lodash 视为外部引入,而不是 the-answer

​ 下面是配置文件:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [resolve({
    // 将自定义选项传递给解析插件
    customResolveOptions: {
      moduleDirectory: 'node_modules'
    }
  })],
  // 指出哪些模块需要被视为外部引入
  external: ['lodash']
};

lodash 现在会被视为外部引入,而不会和你的库打包在一起;

参考

Rollup 中文文档

Rollup 官方插件库

Rollup 打包工具的使用