还在用 rollup 打包库?试试 unbuild 吧

5,633 阅读7分钟

Rollup

Rollup 是一款 JavaScript 模块打包器,它可以将一些零散的 JavaScript 模块打包成一个或多个具有高度优化的 bundle。

与其他打包器如 webpack 和 Parcel 不同,Rollup 更适用于构建 JavaScript 库或组件,因为它能够生成一个纯净的、可重用的模块化代码。

Rollup 采用 ES6 模块和 Tree-shaking 技术,可以消除未使用的代码,从而减小 bundle 的大小。这使得它生成的 bundle 更小、更快、更易于维护和优化,特别适合于在浏览器环境中使用。

Rollup 的配置简单、易于使用,支持多种插件和自定义插件,可以满足各种需求。如果你想构建一个高效、可维护、可重用的 JavaScript 库或组件,那么 Rollup 是一个不错的选择。

Usage

使用 Rollup 打包库也非常简单

install

npm i rollup -D

config

创建 Rollup 配置文件 rollup.config.js

// rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'umd'
  }
}

build

npx rollup --config rollup.config.js

此配置将 src/index.js 打包为一个 UMD 格式的 bundle,并输出到 dist/bundle.js

要使用插件,请在配置文件中导入插件并将其添加到 plugins 中。例如,要使用 Babel 插件将 ES6 代码转换为 ES5 代码:

npm i rollup-plugin-babel -D
// rollup.config.js
import babel from 'rollup-plugin-babel';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'umd'
  },
  plugins: [
    babel({
      exclude: 'node_modules/**'
    })
  ]
}

这将在打包时使用 Babel 插件来转换你的代码。

Features

Rollup 有非常多的优秀的特性

Tree-Shaking

Tree-Shaking 是一种去除 JavaScript 代码中未使用部分的技术,可以减小 bundle 的大小

// math.js
export function square(x) {
  return x * x;
}

// 打包后,这段代码不会出现在 bundle 中
export function cube(x) {
  return x * x * x;
}

// index.js
import { square } from './math.js';

console.log(square(4));

在这个示例中,math.js 导出了两个函数 squarecube,而 index.js 只使用了 square 函数。如果使用 Rollup 和 Tree-Shaking 技术打包这些文件,那么输出的 bundle 将只包含 square 函数和其依赖的代码,而 cube 函数和其依赖的代码将被移除。

Multiple outputs

Rollup 也可以生成多种输出格式,包括 CommonJS、AMD、ES6 模块和 UMD。在 Rollup 配置文件的 output 属性中指定 format 选项即可。例如,要生成一个 CommonJS 格式的输出文件和一个 ES6 模块格式的输出文件,可以这样配置:

// rollup.config.js
export default {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    },
    {
      file: 'dist/bundle.esm.js',
      format: 'es'
    }
  ]
}

这将生成一个 CommonJS 格式的输出文件 dist/bundle.cjs.js 和一个 ES6 模块格式的输出文件 dist/bundle.esm.js。同时也可以使用其他格式,如 AMD 和 UMD。注意,当使用 UMD 格式时,需要指定 name 选项来指定全局变量的名称。例如:

// rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.umd.js',
    format: 'umd',
    name: 'MyLibrary'
  }
}

这将生成一个 UMD 格式的输出文件 dist/bundle.umd.js,并将全局变量命名为 MyLibrary,使得它可以在浏览器和 Node.js 等环境中使用。

Code Splitting

在 Rollup 中,可以使用动态导入语法来实现原生的代码分割。动态导入语法与常规导入语法类似,只是将 import 关键字替换为 import() 函数。

// rollup.config.js
export default {
  input: "src/index.js",
  output: {
    dir: "dist",
  },
}
// src/foo.js
export default 1;
// src/index.js
export default function () {
  // 分割 foo.js
  import("./foo.js").then(({ default: foo }) => console.log(foo));
}

show-lazy.gif

最终打包,./foo.js 会被单独的分割开

Powerful plugins

Rollup 的插件系统非常灵活,可以满足各种需求。Rollup 的插件分为两种类型:转换插件和输出插件。转换插件用于处理源代码,例如将 ES6 代码转换为 ES5 代码、将 CSS 文件转换为 JavaScript 代码等。输出插件用于生成输出文件,例如将 JavaScript 代码写入文件、将代码发送到服务器等。

Rollup 的插件生态非常丰富,有许多优秀的插件可供使用。以下是一些常用的 Rollup 插件:

  • @rollup/plugin-node-resolve:用于解析 Node.js 模块导入语法和第三方模块的依赖关系。使用这个插件可以方便地将 Node.js 模块和第三方模块打包到你的 bundle 中。
  • @rollup/plugin-commonjs:用于将 CommonJS 模块转换为 ES6 模块。使用这个插件可以方便地将 CommonJS 模块打包到你的 bundle 中。
  • rollup-plugin-babel:用于将 ES6+ 代码转换为 ES5 代码。使用这个插件可以方便地在 Rollup 中使用 Babel。
  • rollup-plugin-terser:用于压缩 JavaScript 代码。使用这个插件可以减小输出文件的大小,提高页面加载速度。
  • rollup-plugin-sass:用于将 Sass 文件转换为 CSS 文件。使用这个插件可以方便地在 Rollup 中使用 Sass。
  • rollup-plugin-postcss:用于将 CSS 文件转换为 JavaScript 代码。使用这个插件可以方便地在 Rollup 中使用 PostCSS。
  • rollup-plugin-image:用于将图片转换为 JavaScript 代码。使用这个插件可以方便地在 Rollup 中使用图片。
  • rollup-plugin-json:用于将 JSON 文件转换为 JavaScript 代码。使用这个插件可以方便地在 Rollup 中使用 JSON。

以上插件只是 Rollup 插件生态中的一部分,还有许多其他优秀的插件可供选择。在使用插件时,需要注意不要过度依赖插件,以免增加 bundle 的大小和构建时间。同时,也需要选择适合自己项目的插件,以免出现兼容性和性能问题。

总体而言,Rollup 是非常棒的库打包工具


unbuild

unbuild 也是 js 库打包工具,相比 rollup 有更好的开发体验 🥰

Usage

unbuild 的使用就更简单了

install

npm i unbuild -D

demo

创建 src/index.ts:

export const log = (...args) => { console.log(...args) }

更新 package.json:

{
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  },
  "main": "./dist/index.cjs",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ]
}

build

npx unbuild

打包完成了

当然它也支持配置文件 👉 unbuild/configuration,但大多数情况下你是不需要的

Features

Optimized bundler

unbuild 基于Rollup,集成了 Rollup 生态中非常优秀的插件,开箱即用的支持 typescript,并允许生成commonjsesmodule 格式和类型声明。

这意味着不需要你手动一个个安装插件,配置,调试后才可以进行库开发。

简单查看源码也可以发现内置了很多插件。

image.png

这些插件直接用社区第三方的,也有自己魔改的 (在 src/builder/plugins 中)。

值得一提的是,这里用了 esbuild 来转换 js 代码,比原生 Rollup 还要快。

image.png

Automated config

unbuild 可以自动智能从 package.json 中推断出打包的入口和配置。这意味着大多数情况下,我们甚至不需要配置文件就可以进行正确地打包。

简单查看源码也可以发现,解析预设时未发现预设会回滚到自动预设

image.png image.png

autoPreset 会递归扫描 src 源码目录获取所有源文件路径,同时结合 package.json 推断出 Entries 入口。

image.png

其中 inferEntries 中最有意思的是对 package.jsonbin 的处理 👇

image.png

有了这个,如果我们开发 cli,就不需要单独配置,很 nice 🥰

Bundleless build

集成了 mkdist ,一个轻量的文件转换器。有点像 webpackloader,用来给文件进行预处理的。

而且 mkdist 可以保持原有目录文件的结构,也支持 vuesfc 组件,还能 copy 静态文件。

值得一提的是,我为 copy 静态文件部分提过 pr,用流来跑 copy,支持大型文件,速度很快。

具体可见 👉 perf: copy raw files with stream by markthree · Pull Request #70 · unjs/mkdist

当然携带大型文件到 npm 库中是不推荐的。

Passive watcher

stub 插桩,是我最喜欢的功能。

传统的打包工具,在开发环境采用监听模式时,每改动一次源码就需要打包一次。

例如下边用 Rollup,你会发现每次我保存源文件,都得重新打包一次。

show-lazy.gif

这在小项目开发当然无所谓,但是当我们进行 monorepo 开发时就完蛋了。

可能我们简单改其中一行代码,就会触发多个包一起打包,人直接麻了 🤕

而在 unbuild 中,我们可以使用插桩 👇

npx unbuild --stub

show-lazy.gif

你会发现我们的进程断开了,然后生成了带有 jitibundle

这时我们其实就可以直接使用开发环境的代码了,因为 jiti 可以动态的执行 js 或者 ts 源码,不需要监听模式 🥰

这也意味着我们在写 monorepo 时,只需要运行一次 stub 即可,不会触发重新打包。

Untype Generator

集成了 untyped,可以通过 markdown 和配置对象生成类型。

Secure builds

自动检查各种构建问题,例如潜在的丢失未使用的依赖项。

cli 输出还包括了大小和导出,以进行快速检查。

尾声

后边我可能会介绍如何正确打包库,也欢迎关注 👇

或者关注我的开箱即用的 node 库模板 👉 node-lib-starter