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.rhkb.cn/news/362799.html

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

相关文章

JavaScript--js基础(详细 全面)

目录 前言: JavaScript 是什么?JavaScript 简介 1.JavaScript历史 2.JavaScript 具有以下特点 第一个JavaScript程序 1.在脚本文件中编写JavaScript代码 2.JavaScript代码执行顺序 基本语法 1.变量 2.数据类型 3.算术运算符 4.赋值运算 5.字符串运算符 6…

微服务应用与开发知识点练习【Gateway,OpenFeign,Dubbo,RocketMQ和RabbitMQ,JPA,Redis,Mycat】

一、选择题 【Gateway】 1.Spring Cloud Gateway与Nacos整合实现负载均衡时,路由配置中的URI前缀应该是?(A ) A. lb:// B. nacos:// C. http:// D. discovery:// Spring Cloud Gateway与Nacos整合实现负载均衡时&#xff0c…

华为od 2024 | 什么是华为od,od 薪资待遇,od机试题清单

目录 专栏导读华为OD机试算法题太多了,知识点繁杂,如何刷题更有效率呢? 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、链表5、栈6、滑动窗口7、二叉树8、并查集9、矩阵 三、算法1、基础算法① 贪心思维② 二分查…

鸿蒙面试心得

自疫情过后,java和web前端都进入了冰河时代。年龄、薪资、学历都成了找工作路上躲不开的门槛。 年龄太大pass 薪资要高了pass 学历大专pass 好多好多pass 找工作的路上明明阳关普照,却有一种凄凄惨惨戚戚说不清道不明的“优雅”意境。 如何破局&am…

vite-ts-cesium项目集成mars3d修改相关的包和配置参考

如果vite技术栈下使用原生cesium,请参考下面文件的包和配置修改,想用原生创建的viewer结合我们mars3d的功能的话。 1. package.json文件 "dependencies": {"cesium": "^1.103.0","mars3d": "^3.7.18&quo…

Java——IO流(一)-(8/8):释放资源-try-catch-finally、try-catch-resource

目录 try-catch-finally 介绍 实例演示1 实例演示2 try-catch-resource 介绍 实例演示 try-catch-finally 介绍 普通的释放流的方法可能会因中间的异常或是其他原因,导致程序执行不到释放流的代码就结束了,会有资源浪费的风险,所以建…

Web渗透:文件包含漏洞(part.1)

"文件包含漏洞"(File Inclusion Vulnerability)是一种常见的Web应用程序漏洞,攻击者可以通过这个漏洞在目标系统上包含或执行任意文件。主要有两种类型的文件包含漏洞: 本地文件包含(Local File Inclusion, …

Python学习打卡:day17

day17 笔记来源于:黑马程序员python教程,8天python从入门到精通,学python看这套就够了 目录 day17121、Python 操作 MySQL 基础使用pymysql创建到 MySQL 的数据库链接执行 SQL 语句执行非查询性质的SQL语句执行查询性质的SQL语句 122、Pyth…

全局mixins

一、文章由来 在开发过程中发现在钩子函数位置直接使用dicts就能直接绑定数据了,由此溯源发现了自己的盲区 二、局部使用 // myMixin.js文件 var myMixin {created: function () {this.hello()},methods: {hello: function () {console.log(hello from mixin!)…

隐秘而又复杂的恶意软件:SSLoad

SSLoad 是一种隐秘的恶意软件,主要通过钓鱼邮件打开突破口,收集各种信息再回传给攻击者。近期,研究人员发现 SSLoad 通过诱饵 Word 文档投递恶意 DLL 文件,最终部署 Cobalt Strike。另一种攻击方式是利用钓鱼邮件诱导受害者到 Azu…

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

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

展开说说:Android列表之RecyclerView

RecyclerView 它是从Android5.0出现的全新列表组件,更加强大和灵活。用于显示列表形式 (list) 或者网格形式 (grid) 的数据,替代ListView和GridView成为Android主流的列表组件。可以说Android客户端只要有表格的地方就有RecyclerView。 RecyclerView 内…

技术速递|Visual Studio Code 的 .NET MAUI 扩展现已正式发布

作者:Maddy Montaquila 排版:Alan Wang 今天,我们非常高兴地宣布 .NET MAUI VS Code 扩展插件结束了预览阶段,并将包含一些期待已久的新功能 - 包括 XAML IntelliSense 和 Hot Reload! 什么是 .NET MAUI 扩展插件&…

鸿蒙开发Ability Kit(程序框架服务):【ServiceAbility切换】 组件切换

ServiceAbility切换 FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API,只有系统应用才可以创建。因此,FA模型的ServiceAbility的切换,对于系统应用和三方应用策略有所不同…

4A的「A」会变成AI的「A」吗?

戛纳国际创意节上,广告集团WPP的全球CEO Mark Read 和英国CEO Karen Blackett 解释了WPP如何应对AIGC所带来的「威胁」。同时,Mark Read 与Elon Musk对话,讨论「技术创新的变革力量,人工智能如何重塑创造力、商业和社会&#xff0…

Duix - 硅基数字人SDK

简介 Introduction DUIX(Dialogue User Interface System)是硅基智能打造的AI数字人智能交互平台。通过将数字人交互能力开源,开发者可自行接入多方大模型、语音识别(ASR)、语音合成(TTS)能力,实现数字人实时交互,并在Android和iOS多终端一键部署,让每个开发者可轻松…

基于长短时记忆网络LSTM的TE过程故障诊断(MATLAB R2021B)

实验所用 TE 仿真过程的数据集是网上公开的数据集,该数据集中的训练集和测试集分别包含 20 种故障工况和一种正常工况数据,其中所采集的每个样本信号包含 41 个测量变量和 11 个控制变量,所以每个时刻采集到的样本有 52 个观测变量。 TE 仿真…

IO流

文件 什么是文件 文件流 常用的文件操作 创建文件对象相关构造器和方法 public class FileCreate {public static void main(String[] args) {}//方式 1 new File(String pathname)//根据路径构建一个File对象Testpublic void create01() {String filePath "e:\\news1.…

TMGM:ASIC撤销禁令,TMGM强化合规、重启差价合约服务

TMGM作为差价合约(CFDs)与保证金外汇交易领域的领航者,安全、合规、高效被奉为我集团的终身使命。澳大利亚证券和投资委员会(ASIC)已正式撤销了早前针对TMGM差价合约业务实施的临时止损令。这一误会的解除,…

使用North自部署图床服务

图床 图床可以把图片转为链接,从而方便我们书写、分享博客,目前图床主要分为以下几类: 利用 Git 仓库存储对象存储(OSS、COS、七牛云等)免费公共图床(SM.MS、聚合图床、ImgTP、Postimage等) 但上述图床都…