Vite: 关于Rollup打包

概述

  • Rollup 是一款基于 ES Module 模块规范实现的 JavaScript 打包工具,在前端社区中赫赫有名,同时也在 Vite 的架构体系中发挥着重要作用
  • 不仅是 Vite 生产环境下的打包工具,其插件机制也被 Vite 所兼容,可以说是 Vite 的构建基石

快速上手

  • 首先让我们用 $ npm init -y 新建一个项目,然后安装 rollup 依赖: $ pnpm i rollup

  • 接着新增 src/index.js 和 src/util.js 和 rollup.config.js 三个文件,目录结构如下所示:

    ├── package.json
    ├── pnpm-lock.yaml
    ├── rollup.config.js
    └── src├── index.js└── util.js
    
  • 文件的内容分别如下:

    // src/index.js
    import {add
    } from "./util";console.log(add(1, 2));// src/util.js
    export const add = (a, b) => a + b;
    export const multi = (a, b) => a * b;// rollup.config.js
    // 以下注释是为了能使用 VSCode 的类型提示
    /*** @type { import('rollup').RollupOptions }*/
    const buildOptions = {input: ["src/index.js"],output: {// 产物输出目录dir: "dist/es",// 产物格式format: "esm",},
    };
    export default buildOptions;
    
  • 你可以在 package.json 中加入如下的构建脚本:

    {// rollup 打包命令,`-c` 表示使用配置文件中的配置"build": "rollup -c"
    }
    
  • 接着在终端执行一下 $ npm run build,可以看到如下的命令行信息:

  • OK,现在你已经成功使用 Rollup 打出了第一份产物! 我们可以去 dist/es 目录查看一下产物的内容

    // dist/es/index.js
    // 代码已经打包到一起
    const add = (a, b) => a + b;
    console.log(add(1, 2));
    
  • 同时你也可以发现, util.js 中的 multi 方法并没有被打包到产物中,这是因为 Rollup
    具有天然的 Tree Shaking 功能,可以分析出未使用到的模块并自动擦除。所谓 Tree Shaking (摇树),也是计算机编译原理中 DCE (Dead Code Elimination,即消除无用代码) 技术的一种实现。由于 ES 模块依赖关系是确定的,和运行时状态无关。因此 Rollup 可以在编译阶段分析出依赖关系,对 AST 语法树中没有使用到的节点进行删除,从而实现 Tree Shaking

常用配置


1 ) 多产物配置

  • 在打包 JavaScript 类库的场景中,我们通常需要对外暴露出不同格式的产物供他人使
    用,不仅包括 ESM ,也需要包括诸如 CommonJS 、 UMD 等格式,保证良好的兼容性。那
    么,同一份入口文件,如何让 Rollup 给我们打包出不一样格式的产物呢?我们基于上述
    的配置文件来进行修改:

    // rollup.config.js
    /*** @type { import('rollup').RollupOptions }*/
    const buildOptions = {input: ["src/index.js"],// 将 output 改造成一个数组output: [{dir: "dist/es",format: "esm",},{dir: "dist/cjs",format: "cjs",},],
    };
    export default buildOptions;
    
  • 从代码中可以看到,我们将 output 属性配置成一个数组,数组中每个元素都是一个描述
    对象,决定了不同产物的输出行为。

2 )多入口配置

  • 除了多产物配置,Rollup 中也支持多入口配置,而且通常情况下两者会被结合起来使
    用。接下来,就让我们继续改造之前的配置文件,将 input 设置为一个数组或者一个对
    象,如下所示
    {input: ["src/index.js", "src/util.js"]
    }// 或者
    {input: {index: "src/index.js",util: "src/util.js",},
    }
    
  • 通过执行 $ npm run build 可以发现,所有入口的不同格式产物已经成功输出
  • 如果不同入口对应的打包配置不一样,我们也可以默认导出一个 配置数组 ,如下所示:
    // rollup.config.js
    /*** @type { import('rollup').RollupOptions }*/
    const buildIndexOptions = {input: ["src/index.js"],output: [// 省略 output 配置],
    };
    /*** @type { import('rollup').RollupOptions }*/
    const buildUtilOptions = {input: ["src/util.js"],output: [// 省略 output 配置],
    };
    export default [buildIndexOptions, buildUtilOptions];
    
  • 如果是比较复杂的打包场景(如 Vite 源码本身的打包),我们需要将项目的代码分成几个
    部分,用不同的 Rollup 配置分别打包,这种配置就很有用了。

3 )自定义 output 配置

  • 刚才我们提到了 input 的使用,主要用来声明入口,可以配置成字符串、数组或者对
    象,使用比较简单。而 output 与之相对,用来配置输出的相关信息,常用的配置项如下:

     // 产物输出目录dir: path.resolve(__dirname, 'dist'),// 以下三个配置项都可以使用这些占位符:// 1. [name]: 去除文件后缀后的文件名// 2. [hash]: 根据文件名和文件内容生成的 hash 值// 3. [format]: 产物模块格式,如 es、cjs// 4. [extname]: 产物后缀名(带`.`)// 入口模块的输出文件名entryFileNames: `[name].js`,// 非入口模块(如动态 import)的输出文件名chunkFileNames: 'chunk-[hash].js',// 静态资源文件输出文件名assetFileNames: 'assets/[name]-[hash][extname]',// 产物输出格式,包括`amd`、`cjs`、`es`、`iife`、`umd`、`system`format: 'cjs',// 是否生成 sourcemap 文件sourcemap: true,// 如果是打包出 iife/umd 格式,需要对外暴露出一个全局变量,通过 name 配置变量名name: 'MyBundle',// 全局变量声明globals: {// 项目中可以直接用`$`代替`jquery`jquery: '$'}}
    

4 )依赖 external

  • 对于某些第三方包,有时候我们不想让 Rollup 进行打包,也可以通过 external 进行外部
    化:
{external: ['react', 'react-dom']
}
  • 在 SSR 构建或者使用 ESM CDN 的场景中,这个配置将非常有用,

5 ) 接入插件能力

  • 在 Rollup 的日常使用中,我们难免会遇到一些 Rollup 本身不支持的场景,比如 兼容
    CommonJS 打包 、 注入环境变量 、 配置路径别名 、 压缩产物代码 等等。这个时候就需要我
    们引入相应的 Rollup 插件了。接下来以一个具体的场景为例带大家熟悉一下 Rollup 插
    件的使用。

  • 虽然 Rollup 能够打包 输出 出 CommonJS 格式的产物,但对于 输入 给 Rollup 的代码并
    不支持 CommonJS,仅仅支持 ESM。你可能会说,那我们直接在项目中统一使用 ESM
    规范就可以了啊,这有什么问题呢?需要注意的是,我们不光要考虑项目本身的代码,还
    要考虑第三方依赖。目前为止,还是有不少第三方依赖只有 CommonJS 格式产物而并未
    提供 ESM 产物,比如项目中用到 lodash 时,打包项目会出现这样的报错:

  • 因此,我们需要引入额外的插件去解决这个问题。首先需要安装两个核心的插件包:

    • $ pnpm i @rollup/plugin-node-resolve @rollup/plugin-commonjs
    • @rollup/plugin-node-resolve 是为了允许我们加载第三方依赖,否则像 import React from 'react' 的依赖导入语句将不会被 Rollup 识别。
    • @rollup/plugin-commonjs 的作用是将 CommonJS 格式的代码转换为 ESM 格式
  • 然后让我们在配置文件中导入这些插件:

    // rollup.config.js
    import resolve from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    /*** @type { import('rollup').RollupOptions }*/
    export default {input: ["src/index.js"],output: [{dir: "dist/es",format: "esm",},{dir: "dist/cjs",format: "cjs",},],// 通过 plugins 参数添加插件plugins: [resolve(), commonjs()],
    };
    
  • 现在我们以 lodash 这个只有 CommonJS 产物的第三方包为例测试一下: $ pnpm i lodash

  • 在 src/index.js 加入如下的代码:

    import { merge } from "lodash";
    console.log(merge);
    
  • 然后执行 $ npm run build,你可以发现产物已经正常生成了

  • 在 Rollup 配置文件中, plugins 除了可以与 output 配置在同一级,也可以配置在
    output 参数里面,如:

    // rollup.config.js
    import { terser } from 'rollup-plugin-terser'
    import resolve from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    export default {output: {// 加入 terser 插件,用来压缩代码plugins: [terser()]},plugins: [resolve(), commonjs()]
    }
    
  • 当然,你可以将上述的 terser 插件放到最外层的 plugins 配置中。

  • 需要注意的是, output.plugins 中配置的插件是有一定限制的,只有使用 Output 阶段
    相关钩子(具体内容将在下一节展开)的插件才能够放到这个配置中,可以去这个站点, 查看 Rollup 的 Output 插件列表

  • 另外,这里也给大家分享其它一些比较常用的 Rollup 插件库:

    • @rollup/plugin-json: 支持 .json 的加载,并配合 rollup 的 Tree Shaking 机制去
      掉未使用的部分,进行按需打包
    • @rollup/plugin-babel:在 Rollup 中使用 Babel 进行 JS 代码的语法转译
    • @rollup/plugin-typescript: 支持使用 TypeScript 开发
    • @rollup/plugin-alias:支持别名配置。
    • @rollup/plugin-replace:在 Rollup 进行变量字符串的替换
    • rollup-plugin-visualizer: 对 Rollup 打包产物进行分析,自动生成产物体积可视化分析图

JavaScript API 方式调用

  • 以上我们通过 Rollup 的配置文件结合 rollup -c 完成了 Rollup 的打包过程,但有些场
    景下我们需要基于 Rollup 定制一些打包过程,配置文件就不够灵活了,这时候我们需要
    用到对应 JavaScript API 来调用 Rollup,主要分为 rollup.rolluprollup.watch 两个
    API,接下来我们以具体的例子来学习一下。

  • 首先是 rollup.rollup ,用来一次性地进行 Rollup 打包,你可以新建 build.js ,内容如下:

    // build.js
    const rollup = require("rollup");
    // 常用 inputOptions 配置
    const inputOptions = {input: "./src/index.js",external: [],plugins: []
    };
    const outputOptionsList = [// 常用 outputOptions 配置{dir: 'dist/es',entryFileNames: `[name].[hash].js`,chunkFileNames: 'chunk-[hash].js',assetFileNames: 'assets/[name]-[hash][extname]',format: 'es',sourcemap: true,globals: {lodash: '_'}}// 省略其它的输出配置
    ];async function build() {let bundle;let buildFailed = false;try {// 1. 调用 rollup.rollup 生成 bundle 对象bundle = await rollup.rollup(inputOptions);for (const outputOptions of outputOptionsList) {// 2. 拿到 bundle 对象,根据每一份输出配置,调用 generate 和 write 方法分别生成和写入产物const {output} = await bundle.generate(outputOptions);await bundle.write(outputOptions);}} catch (error) {buildFailed = true;console.error(error);}if (bundle) {// 最后调用 bundle.close 方法结束打包await bundle.close();}process.exit(buildFailed ? 1 : 0);
    }
    build();
    
  • 主要的执行步骤如下:

    • 通过 rollup.rollup 方法,传入 inputOptions ,生成 bundle 对象
    • 调用 bundle 对象的 generate 和 write 方法,传入 outputOptions ,分别完成
      产物和生成和磁盘写入
    • 调用 bundle 对象的 close 方法来结束打包
  • 接着你可以执行 node build.js ,这样,我们就可以完成了以编程的方式来调用 Rollup
    打包的过程。除了通过 rollup.rollup 完成一次性打包,我们也可以通过 rollup.watch 来完成 watch
    模式下的打包,即每次源文件变动后自动进行重新打包。你可以新建 watch.js 文件,内容入下:

    // watch.js
    const rollup = require("rollup");
    const watcher = rollup.watch({// 和 rollup 配置文件中的属性基本一致,只不过多了`watch`配置input: "./src/index.js",output: [{dir: "dist/es",format: "esm",},{dir: "dist/cjs",format: "cjs",},],watch: {exclude: ["node_modules/**"],include: ["src/**"],},
    });
    // 监听 watch 各种事件
    watcher.on("restart", () => {console.log("重新构建...");
    });
    watcher.on("change", (id) => {console.log("发生变动的模块id: ", id);
    });
    watcher.on("event", (e) => {if (e.code === "BUNDLE_END") {console.log("打包信息:", e);}
    });
    
  • 现在你可以通过执行 node watch.js 开启 Rollup 的 watch 打包模式,当你改动一个文件
    后可以看到如下的日志,说明 Rollup 自动进行了重新打包,并触发相应的事件回调函数:

    发生生变动的模块id: /xxx/src/index.js
    重新构建...
    打包信息: {code: 'BUNDLE_END',duration: 10,input: './src/index.js',output: [// 输出产物路径],result: {cache: {/* 产物具体信息 */ },close: [AsyncFunction: close],closed: false,generate: [AsyncFunction: generate],watchFiles: [// 监听文件列表],write: [AsyncFunction: write]}
    }
    
  • 基于如上的两个 JavaScript API 我们可以很方便地在代码中调用 Rollup 的打包流程,相
    比于配置文件有了更多的操作空间,你可以在代码中通过这些 API 对 Rollup 打包过程进
    行定制,甚至是二次开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/36778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构速成--树和二叉树

由于是速成专题,因此内容不会十分全面,只会涵盖考试重点,各学校课程要求不同 ,大家可以按照考纲复习,不全面的内容,可以看一下小编主页数据结构初阶的内容,找到对应专题详细学习一下。 气死了…

东京裸机云服务器怎么用

东京裸机云服务器是一种结合了物理服务器性能和云服务灵活性的高性能计算服务,它为用户提供了高效、安全的计算和存储能力。在了解如何使用东京裸机云服务器之前,需要了解其基本特性和优势。具体分析如下,rak部落小编为您整理发布。 1. **硬件…

代码随想录第36天|动态规划

62. 不同路径 补充: 对二维数组的操作 dp[j][i] 表示到 j,i 有多少种路径递推公式: dp[j][i] dp[j - 1][i] dp[j][i - 1]初始化: dp[0][i] 和 dp[j][0] 都只有1种情况遍历顺序: 由于dp[j][i] 由 上和左的元素推导, 所以采用从左到右、从上到下的遍历顺序 class Solution {…

怎么隐藏宝塔面板左上角绑定的手机号码?

宝塔面板后台的左上角会显示我们绑定的宝塔账号(手机号码),每次截图的时候都要去抹掉这个号码,那么能不能直接将这个手机号码隐藏掉呢? 如上图红色箭头所示的手机号码,其实就是我们绑定的宝塔账号&#xff…

Delphi-2M:基于病史预测未来健康的改进GPT架构

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

CAN-bus总线在冷链运输中的应用

CAN-bus总线在冷链运输中的应用 如图1所示,疫苗冷链是指为保证疫苗从疫苗生产企业到接种单位运转过程中的质量而装备的存储、运输冷藏设施、设备。由于疫苗对温度敏感,从疫苗制造的部门到疫苗使用的现场之间的每一个环节,都可能因温度过高而失效。在储运过程中,一旦温度超…

R语言 | 带P值的相关性热图绘制教程

原文链接:带P值的相关性热图绘制教程 本期教程 往期教程部分内容 **注意:若是在MarkDown格式中无法运行成功,请新建有一个R script文件 ** 一、加载R包 if (!require(corrplot)) install.packages("corrplot") if (!require(Hmi…

【python】PyQt5对象类型的判定,对象删除操作详细解读

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

【离散数学·图论】(复习)

一、基本概念 1.一些基本术语: 2.点u,v邻接(或相邻): 边e称为关联顶点u和v,or e连接u和v; 3.G(V,E)中,顶点v所有邻居的集合:N(v), 成为v的邻域。 4.度 : deg(v) 5.悬挂点:度为1的…

鸿蒙智慧监控解决方案:由Al视觉盒、网络摄像头、管理后台、手机端、NVR等组成

方案架构 整体技术架构由Al视觉盒、网络摄像头、管理后台、手机端、NVR等组成,提供从前端智能监控到后端数据处理的完整闭环。 方案价值 01/节约人力成本 ①AI算法对监控内容进行实时的智能检测,准确率高,有效减少错漏,无需多个…

如何精准分析人形机器人运动数据?

全球“机器换人”进程加速,人形机器人有望成为AI下一个重要落地应用场景;EtherCAT-Analyzer具备分析人形机器人所有关节和电池与主站的通讯信息,快速掌握节点网络状态! 前言 随着人形机器人行业的发展及《中国制造2025》的全面实施,传统的脉冲模式控制很大程度上制约了机…

照明物联网:基于网关的智能照明云监控系统解决方案

智能照明系统就是利用物联网技术,将同一空间的照明、空调、新风、排风等系统共同接入物联网平台,实现了“设备互联、数据互通”的智慧物联能力。照明数据、环境监测数据通过网关上传云端,在云端进行统计分析并将结果通过各种终端共享&#xf…

JVM专题十三:总结与整理(持续更新)

图解JVM JVM与Java体系结构 JVM垃圾回收算法 JVM垃圾回收器 图解JVM主要是放了前面12个章节的我们给大家画的图,做了整体的汇总,大家可以根据图区回忆我们所说的内容,查缺补漏。 实战经验 1、项目中数据量多少,QPS与TPS最高多少…

充电站,正在杀死加油站

最近,深圳公布了一组数据,深圳的超级充电站数量已超过传统加油站数量,充电枪数量也已超过加油枪数量。 从全国范围看,加油站关停的速度在加快。 充电站正在杀死加油站。 加油站,未来何去何从? 01. 减少 我…

FFmpeg教程-二-代码实现录音

目录 一,实现步骤 二,具体实现 1,注册设备 2,获取输入格式对象 3,打开设备 4,采集数据 一,实现步骤 二,具体实现 1,注册设备 // 初始化libavdevice并注册所有输入…

操作系统-中断和异常

中断和异常 用户态:普通应用程序运行在用户态,有很多权限限制 内核态:操作系统运行在内核态,有完全的权限访问和管理所有资源(硬件,内存) 中断的作用 把CPU从用户态变内核态 异常&#xff08…

C++ 教程 - 07 类的静态成员

文章目录 静态成员 静态成员 使用static修饰的成员; 静态的成员变量; 仅保留一份副本,不管创建多少个实例对象,都共享这一份数据;类、对象均可以调用;类外重新声明,并通过类初始化;…

C语言:哈希表

1、文章声明: 本文是基于链地址法建立的哈希表。文章中若存在错误,欢迎各路大佬指正。本文涉及二级指针,链表等内容。该方面的知识点,可以参考文章: 数据结构:单链表的相关操作-CSDN博客 C语言&#xff…

什么是软件架构的 4+1视图?如何运用?有什么优缺点?

一、41视图的起源 41视图最早由Philippe Kruchten提出。他在1995年的《IEEE Software》上发表了题为《The 41 View Model of Architecture》的论文,这一论文的发表引起了业界的极大关注,并最终被RUP(Rational Unified Process,统一…

【力扣】有效的字母异位词

🔥博客主页: 我要成为C领域大神🎥系列专栏:【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 给定两个字符串 s …