Rollup是一个用于JavaScript的模块打包工具,使用JavaScriptES6版本中包含的新标准化代码模块格式,而不是以前的CommonJSAMD等特殊解决方案。让你像ES 模块一样,在所有场景获得原生支持,自由无缝地组合你最喜欢的库中最有用的个别函数

系列文章:


一、安装使用

作为 全局命令行工具 使用

$ npm install --global rollup

二、基础示例

场景描述:利用ES MODULE机制,封装了一个@js/sdk包。其中,要求对于?.类似语法,进行ES5的编译处理。@js/sdk具体要求如下:

  • sdk/src/index.js导出如下:
export {
    modelA,
    modelB,
    // ...
} from './zmini/index' 
  • sdk/src/utils/util.js等使用?.语法文件,进行编译:
const resError = {
    platform,
    code: error?.code,
    errorCode: error?.subCode,
    message: error?.message
}
  • 前期主要利用bable,进行构建,所以需要兼容bable相关逻辑。历史package.json核心如下:
{
    "scripts": {
        "build": "babel src -d lib" // 构建命令
    },
    "devDependencies": { 
        "@babel/cli": "^7.23.4", // babel 依赖
        "@babel/core": "^7.23.5",
        "@babel/preset-env": "^7.23.5"
    }
}

1.安装依赖和文件配置

1.1 安装依赖

安装必要的依赖包

$ npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel @rollup/plugin-terser @babel/core @babel/preset-env
  • @rollup/plugin-node-resolve: 允许 Rollup 查找并打包 node_modules 中的模块。
  • @rollup/plugin-commonjs: 使 Rollup 可以转换 CommonJS 模块为 ES6 模块。
  • @rollup/plugin-babel: 集成 Babel 以便处理现代 JavaScript 语法,并将其转换为支持更老的环境(如 Node.js 12+)的代码。
  • @rollup/plugin-terser: 用于压缩输出的代码,以减小打包后的体积。
  • babel.config.js: 配置 Babel,使其使用 @babel/preset-env 来根据目标环境编译代码,并使用 core-js 自动引入 polyfill 以支持可选链语法和其他现代 JavaScript 特性。

1.2 Rollup 配置文件

创建一个 rollup.config.js 文件,内容如下:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import { terser } from '@rollup/plugin-terser';
// import terser from '@rollup/plugin-terser'; // 某些版本是默认导出

export default {
    input: 'src/index.js', // 入口文件
    output: [
    //     {
    //         file: 'lib/bundle.cjs.js', // CommonJS 输出
    //         format: 'cjs',
    //         sourcemap: true,
    //    },
       {
            file: 'lib/bundle.esm.js', // ES Module 输出
            format: 'es',
            sourcemap: true,
       }
    ],
    external: ['@js/base-sdk'], // 忽略package.json中的依赖,避免被打包到libs
    plugins: [
        resolve(), // 解析模块
        commonjs(), // 处理 CommonJS 模块
        babel({
            babelHelpers: 'bundled',
            exclude: 'node_modules/**', // 只编译我们的源码
            presets: [
                [
                    '@babel/preset-env',
                    {
                       targets: {
                         node: '12', // 确保支持 node 12+
                       },
                       useBuiltIns: 'usage',
                       corejs: 3, // 自动引入 polyfill
                    },
                ],
            ],
       }),
       terser() // 代码压缩
    ],
};

1.3 Babel 配置文件

创建一个 .babelrcbabel.config.js 文件配置Babel,确保编译目标支持较老的 Node.js 环境:

{
    "presets": [
        [
            "@babel/preset-env",
            {
               "targets": {
                 "node": "12"
               },
               "useBuiltIns": "usage",
               "corejs": 3
            }
        ]
    ]
}

2.构建命令

在你的 sdk/package.json 中,更新 scripts 部分,使用 Rollup 构建你的包:

{
    "type": "module",
    // 第一种方式:使用 main 和 module 两个配置
    // "main": "./lib/bundle.cjs.js",  // CommonJS 入口
    // "module": "./lib/bundle.esm.js", // ES Module 入口
    // 第二种:只使用main —— 推荐这种
    "main": "./lib/bundle.esm.js",  // 入口配置ES Module
    "scripts": {
        "build": "rollup -c"
    }
}

终端运行以下命令来构建你的 SDK 包:

$ npm run build

构建完成后,生成的文件将在 lib/ 目录下,分别以 CommonJS 和 ES Module 格式输出。这些文件将兼容 Node.js 12 及更高版本。


三、常见问题

1. 想要在node 16环境下,使用rollup构建,请问我要如何处理?

异常如下:

lianpf@lianpf-PC:~/xxx/@js/sdk$ npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel @rollup/plugin-terser @babel/core @babel/preset-env
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'rollup@4.21.0',
npm WARN EBADENGINE   required: { node: '>=18.0.0', npm: '>=8.0.0' },
npm WARN EBADENGINE   current: { node: 'v16.17.1', npm: '8.15.0' }
npm WARN EBADENGINE }

added 63 packages, removed 4 packages, and changed 93 packages in 15s

20 packages are looking for funding
  run `npm fund` for details

处理方式:

这个错误是由于你当前使用的 Node.js 版本 (v16.17.1) 和 Rollup 的最新版本 (4.21.0) 之间存在不兼容问题。Rollup 4.21.0 要求 Node.js 版本为 18.0.0 或更高版本,而你目前使用的版本是 16.17.1,因此出现了 EBADENGINE 警告。

解决方案:

  1. 升级 Node.js 版本
    最简单的解决方法是升级你的 Node.js 版本到 18.0.0 或更高。你可以使用 nnvm 来管理和切换 Node.js 版本。

    • 使用 n 工具升级:

      sudo npm install -g n
      sudo n stable
      
    • 使用 nvm 工具升级:

      nvm install 18
      nvm use 18
      

    然后再次运行 npm install 来安装依赖。

  2. 使用支持 Node.js 16 的 Rollup 版本
    如果你无法升级 Node.js 版本(比如项目需要兼容 Node.js 16),你可以安装一个旧版本的 Rollup。比如,Rollup 3.x 支持 Node.js 16。

    执行以下命令安装较旧的 Rollup 版本:

    npm install --save-dev rollup@3 @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel @rollup/plugin-terser @babel/core @babel/preset-env
    

2. @rollup/plugin-commonjs主要处理什么语法?什么情况下,output才需要输出CommonJS?

@rollup/plugin-commonjs插件主要处理CommonJS(CJS)模块的语法,特别在Node.js环境中。它使用require()函数来同步地加载其他模块,并通过 module.exportsexports对象来导出模块的功能。

示例:
假设你有一个CommonJS模块(math.js),它定义了一些数学函数并导出它们:

// math.js (CommonJS)  
function add(a, b) {  
  return a + b;  
}  
  
function subtract(a, b) {  
  return a - b;  
}  
  
module.exports = { add, subtract };

然后,你想在另一个使用 ES 模块语法的文件中导入这个 CommonJS 模块。但由于 Rollup 默认只理解 ES 模块,它无法直接解析 require() 调用。这就是 @rollup/plugin-commonjs 插件发挥作用的地方。它可以将 CommonJS 模块转换为 Rollup 可以理解的 ES 模块格式。

何时需要输出 CommonJS格式(如 .cjs.js,主要有以下几种情况:

背景:在Node.js中,版本12才开始对ES 模块有基础的支持

  • 兼容性:某些库或应用程序(因为需要支持较旧的Node.js版本或特定环境)不支持ES 模块
  • 生态系统支持:输出CommonJS 格式可确保你的库或应用程序,被基于CommonJS的工具和构建系统(如:Node.js包管理工具npm)所消费。
  • 性能考虑:在某些情况下,CommonJS 模块ES 模块加载得更快,因为它们通常使用同步的require()调用。然而,在现代Node.js 版本中通常差异很小,且随着ES 模块性能的提升而逐渐消失。
  • 外部使用场景:外部使用 CommonJS 模块的场景非常广泛,包括但不限于:
    • Node.js 项目:在 Node.js 中,直到版本 12 才开始对 ES 模块有基本的支持,而 CommonJS 仍然是主流。
    • 遗留代码库:许多旧的项目和库仍然使用 CommonJS,因为它们是在 ES 模块成为标准之前就开发的。
    • 混合项目:在一些项目中,你可能需要同时支持 ES 模块和 CommonJS 模块,以便能够兼容不同的环境或工具链。
    • npm 包:由于 npm 是 JavaScript 社区中最大的包注册表之一,并且许多包仍然使用 CommonJS,因此如果你正在开发一个 npm 包,并希望它能够被尽可能多的项目所使用,那么通常需要支持 CommonJS 格式。

3. 如何保持开发和构建文件目录一致,以便于调试和排查问题?

示例:
假设你的 src 目录结构如下:

src/
  index.js
  utils/
    helper.js
  components/
    button.js
    modal.js

在打包后,你的 lib 目录结构和src保持一致:

lib/
  index.js
  utils/
    helper.js
  components/
    button.js
    modal.js

优化 rollup.config.js

  • dir 选项:在 output 配置中使用 dir 而不是 fileRollup会将输出的文件放在指定的目录中,而不是生成单一的文件
  • preserveModules:启用此选项,Rollup将保留模块的原始结构,而不是将所有文件打包到一个文件中(每个模块会生成一个独立的文件
  • preserveModulesRoot:指定保留的模块目录的根目录。此例中,src 目录的层级结构会保持到 lib
export default {
  input: 'src/index.js', // 入口文件(可支持多个入口)
  output: [
    {
      dir: 'lib', // 输出文件夹
      format: 'es', // ES Module 输出
      sourcemap: true,
      preserveModules: true, // 保持模块结构
      preserveModulesRoot: 'src', // 保持 src 目录结构
    },
  ],
  external: ['@js/base-sdk'], // 忽略package.json中的依赖,避免被打包到libs
  plugins: [
    // ...
  ],
};

总结

常用(最终)配置

待补充…


参考


最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~

微信公众号

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!