新闻中心

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

2025-10-29
浏览次数:
返回列表

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

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

背景

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

万相营造 万相营造

阿里妈妈推出的AI电商营销工具

万相营造 168 查看详情 万相营造
/** 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: 'es2025',
    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: 'es2025',
    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 特性,我们可以轻松地为 J*aScript 项目同时生成 IIFE 和 ESM 两种格式的包,并确保每种格式的包都只包含必要的代码。这可以显著减小构建产物的大小,并提高应用程序的性能。

注意事项:

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

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

以上就是使用 esbuild 混合插件为多个文件打包 IIFE 和单个 ESM 包的详细内容,更多请关注其它相关文章!


# java  # 大连seo查询方法  # 两江新区网站的推广  # 泽州专业网站推广  # 青浦区网站关键词优化  # 抚顺网站推广威杏hfqjwl下拉  # 美团企业的营销推广方式  # 做搜狗手机网站优化首  # 北海推广网站推荐  # 亭湖网站推广哪家好  # 都只  # 怎么做  # 自定义  # 我们可以  # 而在  # 全局变量  # 移除  # 两种  # 多个  # 如何使用  # win  # js  # javascript  # 本溪网站关键词优化排名 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 动漫共和国防屏蔽稳定域名-动漫共和国官方正版直达通道  Python多线程中正确使用sigwait处理SIGALRM信号  12306选座怎么选到商务座_12306商务座选择与配置说明  如何在更新Composer依赖后自动运行测试_使用post-update-cmd钩子触发PHPUnit  优化HTML表单样式:解决输入框焦点跳动与元素间距问题  在J*a中如何隐藏复杂性_使用门面模式组织对象交互  Adobe PDF表单中利用J*aScript解析与格式化日期组件的教程  Linux如何构建多环境配置管理_Linux多环境配置方案  React Router v6 教程:构建认证保护的私有路由与重定向策略  Go语言中JSON数据解析与字段访问教程  TikTok搜索结果不显示如何解决 TikTok搜索刷新优化方法  Golang如何使用bytes.Split分割字节切片_Golang bytes切片分割方法  Promise错误处理:在catch后终止链式then执行的策略  漫画星球免费下拉式入口 漫画星球免费漫画在线阅读网站  ExcelARRAYTOTEXT函数怎么自定义分隔符输出数组文本_ARRAYTOTEXT实现动态生成SQL语句  2026春节假期票务安排_2026春节放假购票指南  win11如何卸载Windows更新补丁 Win11解决更新导致系统不稳定的问题【修复】  SteamMachine定价或为699美元 大家想入手吗?  b站怎么看视频的弹幕数量_b站弹幕数量查看方法  汽水音乐车机版8.9下载 汽水音乐车机版8.9版本安装入口  Tabulator表格日期时间排序问题及自定义解决方案  excel如何生成目录 excel一键生成工作表目录超链接  微信怎么把收藏的内容分类管理 微信收藏内容标签分类方法  必由学登录入口 必由学官方网站在线访问链接  Go语言中JSON数据解码与字段访问指南  Highcharts 雷达图径向轴标签定制指南:利用多Y轴实现数值标注  163邮箱登录密码 163邮箱忘记密码找回  小红书网页版入口链接分享 小红书官网直接进  在Qt QML中通过Python字典动态更新TextEdit内容的教程  搜狗浏览器如何使用密码生成器创建强密码 搜狗浏览器内置密码安全工具  Lar*el递归关系中排除子孙节点的策略  win11怎么查看应用耗电情况 Win11电池设置查看应用能耗排行榜【优化】  J*a里如何实现线程安全的懒加载单例_懒加载单例实现方法解析  微博网页版怎么开启两步验证_微博网页版账号安全两步验证设置方法  极兔快递快件信息查询系统 极兔快递官网运单号追踪  Lar*el 8 多关键词数据库搜索优化实践  动漫岛观看全网网 动漫岛在线正版动漫入口  解决Flask中Quill编辑器内容提交失败及TypeError的指南  Go Martini框架:动态服务解码后的图片内容  汽水音乐在线解析 汽水音乐在线解析入口  Selenium Python中处理点击后新窗口加载冻结问题的策略与实践  邮政快递单号查询入口 邮政快递物流信息在线查询入口  J*a 递归快速排序中静态变量的状态管理与陷阱  Python实现多节点属性重叠度分析教程  优化 Jest 模拟:强制未实现函数抛出错误以提升测试效率  在Blazor WebAssembly应用中动态注入客户端特定指标代码的策略  Golang如何实现Web文件静态资源服务器_Golang静态资源服务器开发与实践  WordPress插件开发:正确注册卸载钩子与避免常见陷阱  解决 Express.js 中 PUT 请求密码修改失败的路由配置指南  Django表单提交验证失败后保持字段值不刷新 

搜索