esbuild 默认会将所有导入的模块封装在独立作用域中,这使得通过 import 加载的 jQuery 无法被依赖全局变量的传统脚本(如 UMD/AMD 模块或无模块声明的 JS 文件)直接访问;必须借助 inject、banner 或全局挂载等机制显式暴露全局变量到 window 对象上,才能解决 esbuild 中 jQuery 未定义的问题。
场景是这样的:你使用 esbuild 打包项目,通过 import 引入了 jQuery,但页面上运行的传统脚本报错“jQuery is not defined”。原因其实很简单——esbuild 会将所有模块封闭在独立作用域内,即使你写了 import jQuery from './jquery-3.7.1.js',这个 jQuery 变量也仅在你当前模块中生效,并不会自动挂载到 window 对象上。而像 otherFile.js 这类传统脚本(直接调用 jQuery.fn.xxx 或 $() 的方式),它们依赖全局的 window.jQuery 和 window.$,因此必然导致执行失败。
很多开发者会想:“那我先 import 再手动赋值 window.jQuery = jQuery 不就能解决吗?” 理论上可行,但实际开发中容易踩坑——ES 模块的执行顺序和作用域隔离可能导致赋值语句在 otherFile.js 执行前尚未生效,尤其当 otherFile.js 是非模块脚本且被动态加载或异步执行时,时机问题更加难以把控。
下面直接给出三个生产环境可用的解决方案,按推荐度排序。
✅ 方案一:使用 inject 选项(推荐|最可靠)
inject 是 esbuild 官方提供的标准化方案,它允许你将一个导出全局变量的模块“注入”到每个入口文件顶部,确保 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 少一个文件,但可读性略差,且如果有多入口则需要重复配置。
✅ 方案三:改用
然后在 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)。
