讲师中心 微信公众号
AI工具推荐 视频效率加速

使用 esbuild 混合插件为多个文件打包 IIFE 和单个 ESM 包

风杰吖_3021

风杰吖_3021

发布时间:2025-10-29 13:39:27

|

650人浏览过

|

来源于php中文网

原创

使用 esbuild 混合插件为多个文件打包 iife 和单个 esm 包

本文介绍如何使用 esbuild 插件和 `define` 特性,为 JavaScript 项目同时生成 IIFE (Immediately Invoked Function Expression) 和 ESM (ECMAScript Module) 两种格式的包。通过自定义插件移除 IIFE 构建中的 imports,并利用 `define` 标志在代码中区分不同构建环境,最终实现代码的按需引入和精简输出,从而优化构建产物的大小和性能。

背景

在开发 JavaScript 库或框架时,通常需要支持多种模块化格式,以满足不同用户的需求。例如,一些用户可能仍然喜欢使用传统的 <script> 标签引入 IIFE 格式的包,而另一些用户则更倾向于使用 ESM 格式的包,以便利用现代 JavaScript 的特性,如 tree shaking。</script>

本文将以 SlickGrid 项目为例,介绍如何使用 esbuild 插件和 define 特性,同时生成 IIFE 和 ESM 两种格式的包,并确保每种格式的包都只包含必要的代码,从而优化构建产物的大小和性能。

解决方案

该解决方案的核心在于使用 esbuild 插件来处理 IIFE 构建中的 imports,并使用 define 特性在代码中区分不同的构建环境。

1. 自定义 esbuild 插件移除 Imports

首先,我们需要创建一个 esbuild 插件,用于在 IIFE 构建中移除所有的 import 语句。这是因为 IIFE 格式的包通常依赖于全局变量,而不是模块导入。

import { build } from 'esbuild';

const removeImportsPlugin= {
    name: 'remove-imports-plugin',
    setup(build) {
      build.onResolve({ filter: /.*/ }, (args) => {
        if (args.kind !== 'entry-point') {
          return { path: args.path + '.js', namespace: 'import-ns' }
        }
      });
      build.onLoad({ filter: /.*/, namespace: 'import-ns' }, () => ({
        contents: `// empty string, do nothing`,
        loader: 'js',
      }));
    }
};

这个插件使用了 onResolve 和 onLoad 钩子。onResolve 钩子拦截所有非入口文件的导入请求,并将其重定向到 import-ns 命名空间。onLoad 钩子则拦截 import-ns 命名空间下的所有文件加载请求,并返回一个空字符串,从而有效地移除了所有的 import 语句。

2. 使用 define 特性区分构建环境

接下来,我们需要使用 esbuild 的 define 特性,在代码中定义一个全局变量,用于区分 IIFE 和 ESM 构建环境。

java-api-doc-generator
java-api-doc-generator

分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。

下载
/** build as iife, every file will be bundled separately */
export async function buildIifeFile(file) {
  build({
    entryPoints: [file],
    format: 'iife',
    // add Slick to global only when filename `slick.core.js` is detected
    globalName: /slick.core.js/.test(file) ? 'Slick' : undefined,
    define: { IIFE_ONLY: 'true' },
    outfile: `dist/browser/${file.replace(/.[j|t]s/, '')}.js`,
    plugins: [removeImportsPlugin],
  });
}

// bundle in ESM format into single file index.js
export function buildEsm() {
  build({
    entryPoints: ['index.js'],
    format: 'esm',
    target: 'es2020',
    treeShaking: true,
    define: { IIFE_ONLY: 'false' },
    outdir: `dist/esm`,
  });
}

在 IIFE 构建中,我们将 IIFE_ONLY 定义为 'true',而在 ESM 构建中,我们将 IIFE_ONLY 定义为 'false'。

3. 在代码中使用 IIFE_ONLY 变量

现在,我们可以在代码中使用 IIFE_ONLY 变量来区分不同的构建环境。例如:

// imports will be auto-dropped in iife by custom plugin
import { SlickEvent as SlickEvent_, Utils as Utils_ } from '../slick.core';

// for (iife) load `Slick` methods from global window object, or use imports for (cjs/esm)
const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;
const Utils = IIFE_ONLY ? Slick.Utils : Utils_;

// ...

// then use it normally in the code...
const options = Utils.extend(true, {}, defaults, options);

在 IIFE 构建中,IIFE_ONLY 为 'true',因此 SlickEvent 和 Utils 将从全局 Slick 对象中获取。而在 ESM 构建中,IIFE_ONLY 为 'false',因此 SlickEvent 和 Utils 将从 import 语句中获取。

示例

以下是一个完整的示例,展示如何使用 esbuild 插件和 define 特性同时生成 IIFE 和 ESM 格式的包:

// esbuild.config.js
import { build } from 'esbuild';

const removeImportsPlugin= {
    name: 'remove-imports-plugin',
    setup(build) {
      build.onResolve({ filter: /.*/ }, (args) => {
        if (args.kind !== 'entry-point') {
          return { path: args.path + '.js', namespace: 'import-ns' }
        }
      });
      build.onLoad({ filter: /.*/, namespace: 'import-ns' }, () => ({
        contents: `// empty string, do nothing`,
        loader: 'js',
      }));
    }
};

/** build as iife, every file will be bundled separately */
export async function buildIifeFile(file) {
  build({
    entryPoints: [file],
    format: 'iife',
    // add Slick to global only when filename `slick.core.js` is detected
    globalName: /slick.core.js/.test(file) ? 'Slick' : undefined,
    define: { IIFE_ONLY: 'true' },
    outfile: `dist/browser/${file.replace(/.[j|t]s/, '')}.js`,
    plugins: [removeImportsPlugin],
  });
}

// bundle in ESM format into single file index.js
export function buildEsm() {
  build({
    entryPoints: ['index.js'],
    format: 'esm',
    target: 'es2020',
    treeShaking: true,
    define: { IIFE_ONLY: 'false' },
    outdir: `dist/esm`,
  });
}

// index.js
// imports will be auto-dropped in iife by custom plugin
import { SlickEvent as SlickEvent_, Utils as Utils_ } from '../slick.core';

// for (iife) load `Slick` methods from global window object, or use imports for (cjs/esm)
const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;
const Utils = IIFE_ONLY ? Slick.Utils : Utils_;

// ...

// then use it normally in the code...
const options = Utils.extend(true, {}, defaults, options);

总结

通过使用 esbuild 插件和 define 特性,我们可以轻松地为 JavaScript 项目同时生成 IIFE 和 ESM 两种格式的包,并确保每种格式的包都只包含必要的代码。这可以显著减小构建产物的大小,并提高应用程序的性能。

注意事项:

  • define 特性会将字符串值视为 JavaScript 代码,因此需要确保字符串值的格式正确。
  • 在 IIFE 构建中,需要确保所有的依赖项都已通过 <script> 标签加载到全局环境中。</script>
  • 可以根据实际需求调整插件和 define 特性的配置。

希望本文能够帮助你更好地理解如何使用 esbuild 同时生成 IIFE 和 ESM 格式的包。

热门AI工具

更多
小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

相关专题

更多
typedef和define区别
typedef和define区别

typedef和define区别在类型检查、作用范围、可读性、错误处理和内存占用等。本专题为大家提供typedef和define相关的文章、下载、课程内容,供大家免费下载体验。

313

2023.09.26

define的用法
define的用法

define用法:1、定义常量;2、定义函数宏:3、定义条件编译;4、定义多行宏。更多关于define的用法的内容,大家可以阅读本专题下的文章。

619

2023.10.11

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

4865

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

2050

2025.09.18

c++ 全局变量
c++ 全局变量

本专题整合了c++全局变量的使用、定义、作用域等等内容,阅读专题下面的文章了解更多详细内容。

199

2026.03.17

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1818

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2884

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6204

2023.10.24

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.4万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn