esbuild正确暴露jQuery供依赖脚本使用
esbuild默认将模块封装在独立作用域,通过import加载的jQuery无法被传统脚本访问。需使用inject、banner或全局挂载显式暴露window.jQuery。推荐inject方案,可确保注入时机可靠,避免竞态问题,且配置简洁,是常见场景下的最佳实践。
esbuild 默认将导入的模块封装在独立作用域中,导致通过 import 加载的 jQuery 无法被全局依赖脚本(如传统 UMD/AMD 或无模块声明的 JS 文件)访问;需通过 inject、banner 或全局挂载等机制显式暴露全局变量。
在 esbuild 打包过程中,当你通过 import jQuery from './jquery-3.7.1.js' 引入 jQuery 时,该变量仅存在于当前模块的独立作用域内,不会自动挂载到 window 对象上。因此,那些依赖全局 window.jQuery 和 window.$ 的传统脚本(例如直接调用 jQuery.fn.xxx 或 $() 的 otherFile.js)会报错“jQuery is not defined”。
部分开发者尝试在 import 后手动赋值 window.jQuery = jQuery,但这种方法存在隐患:ES 模块的执行顺序与作用域隔离可能导致赋值语句在依赖脚本执行前尚未生效,尤其当 otherFile.js 以非模块方式动态加载或异步执行时,时机难以把控。
以下提供三种生产级方案,按推荐度排序。
方案一:使用 inject 选项(推荐|最可靠)
esbuild 的 inject 选项是官方提供的标准化方案,能够将导出全局变量的模块“注入”到每个入口文件顶部,确保 jQuery 在任何依赖代码执行前完成挂载。具体操作如下:
// inject-jquery.js import jQuery from './jquery-3.7.1.js'; window.jQuery = window.$ = jQuery;
构建命令:
esbuild src/index.js \ --bundle \ --inject:./inject-jquery.js \ --outfile=dist/bundle.js
esbuild 会将 inject-jquery.js 的内容前置插入,生成的 bundle 示例如下:
// 自动生成的 bundle 开头
var jQuery = (() => { /* jquery source */ })();
window.jQuery = window.$ = jQuery;
// 后续你的其他 import...
import './otherFile.js'; // 此时 window.jQuery 已存在
该方案的优势在于:注入时机由 esbuild 确保,不会出现竞态问题,且适用于所有需要全局暴露的库(如 Lodash、Moment.js),一劳永逸。
方案二:使用 banner 注入全局声明(轻量简洁)
如果 jQuery 已通过 CDN 引入,或只需声明全局变量名(配合 --external:jquery),可使用 banner 选项:
esbuild src/index.js \ --bundle \ --banner:js="import jQuery from './jquery-3.7.1.js'; window.jQuery = window.$ = jQuery;" \ --outfile=dist/bundle.js
注意,banner 中的代码会放在 bundle 最顶端执行,但前提是 jquery-3.7.1.js 必须是可解析的本地模块(不能是外部 CDN 地址)。此方法比 inject 少一个文件,但可读性略差,且多入口时需重复配置。
方案三:改用
若项目允许混合加载,最简单的方式是在 HTML 中优先加载 jQuery:
然后在 index.js 中移除 import jQuery,改为类型声明(避免 TypeScript 报错):
// @ts-ignore declare const jQuery: any; // 或使用 declare global(TypeScript 环境)
该方案兼容性最强,但牺牲了模块化统一性,适合旧项目逐步迁移的场景。
注意事项
- 避免使用
import jQuery from '...'; window.jQuery = jQuery; import './otherFile.js';这种写法,因为 ES 模块的静态分析与执行顺序可能导致otherFile.js(若为 CommonJS/UMD 格式)在window.jQuery赋值前执行,引发 undefined 错误。 -
inject是 esbuild 官方支持的标准化方案,适用于所有需要全局暴露的库(如 Lodash、Moment.js)。 - 检查
otherFile.js是否包含'use strict'或export/import语句。如果是现代模块,应改用import $ from 'jquery'并统一模块化管理,彻底告别全局变量。 - 使用
--minify时,确保 jQuery 构建版本已压缩且保留window挂载逻辑(官方 dist 版本默认支持)。
总结:esbuild 的模块封闭性是其设计特性,并非缺陷。要桥接传统脚本生态,必须主动、提前、可靠地暴露全局变量——inject 是最符合工程规范的解决方案。
