`wp_enqueue_script` 依赖填错顺序,我的前端脚本在控制台报了三小时 "jQuery is not defined"
上周给插件加后台可视化配置页,前端用了 Vue3 + 自己封装的 WP Ajax 桥。本地跑得好好的,打包发到测试站,F12 一片红,jQuery is not defined、$ is not defined 交替出现。最诡异的是,刷新几次偶尔又能跑通,搞得我以为是 CDN 抖动。
三小时后才发现,问题根本不在 jQuery 本身,而在 wp_enqueue_script 的 $deps 数组里我填的是 ['jquery'],但我的桥接脚本实际写的是 jQuery.ajax——而 WP 后台默认加载的是 jQuery 的 noConflict 模式,且我另一个依赖 wp-util 的脚本(里面用了 wp.ajax.send)被我不小心排在了自己脚本后面。
先贴我当时的错误代码,典型的"依赖写了但没写对、顺序写了但没写全":
// ❌ 错误写法:依赖声明不全 + 版本号缓存穿透 + 加载顺序靠运气
wp_enqueue_script(
'my-vue-admin',
plugin_dir_url( __FILE__ ) . 'dist/admin.js',
['jquery'], // 漏了 wp-util,且 jquery 其实不需要显式声明(后台已加载)
'1.0.0', // 版本号写死,浏览器缓存不刷新
true // 放 footer,但 wp-util 可能在 header 就执行了
);
wp_enqueue_script(
'my-ajax-bridge',
plugin_dir_url( __FILE__ ) . 'dist/bridge.js',
['my-vue-admin'], // 桥接依赖 Vue 实例,但 Vue 是在 admin.js 里动态 import 的
'1.0.0',
true
);
问题一层层剥:
第一,jquery 在后台是默认加载的,显式声明反而容易掩盖真正的依赖关系。 我真正需要的是 wp-util(它自带 wp.ajax.send 的 jQuery 封装),但 $deps 里没写。结果浏览器偶尔因为其他插件先加载了 wp-util 而"碰巧跑通",刷新后缓存顺序一变就炸。
第二,版本号写死 '1.0.0',测试时浏览器拿着旧 js 不放手。 我改了代码但 URL 没变,强制刷新都救不了,因为 CDN 边缘节点也缓存了。这导致我一度以为"代码修好了但环境有问题",在错误方向上浪费了四十分钟。
第三,wp-util 默认在 header 加载,我的脚本放 footer,但桥接脚本里直接调了 wp.ajax.send。 如果 wp-util 因其他原因延迟(比如被另一个插件用 wp_dequeue_script 搞了),wp 对象不存在,报错信息却是 jQuery is not defined——因为 wp.ajax.send 内部捕获异常时抛的是 jQuery 相关的兜底错误,完全误导排查方向。
正确的写法我后来拆成了三步:先理清楚"谁依赖谁",再用文件修改时间戳当版本号,最后用 wp_script_is 做防御性检查:
// ✅ 正确写法:依赖精确、版本防缓存、顺序可控、失败可降级
$js_path = plugin_dir_path( __FILE__ ) . 'dist/admin.js';
$js_url = plugin_dir_url( __FILE__ ) . 'dist/admin.js';
$ver = filemtime( $js_path ); // 文件修改即刷新缓存
// 1. 先确保 wp-util 可用(它比 jquery 更关键)
wp_enqueue_script( 'wp-util' );
// 2. 主脚本:明确依赖 wp-util,放 footer
wp_enqueue_script(
'my-admin-core',
$js_url,
['wp-util'], // 精确依赖:我需要的是 wp.ajax 而不是裸 jQuery
$ver,
true
);
// 3. 桥接脚本:依赖主脚本,且用 wp_add_inline_script 做前置检查
wp_enqueue_script(
'my-ajax-bridge',
plugin_dir_url( __FILE__ ) . 'dist/bridge.js',
['my-admin-core'],
$ver,
true
);
// 4. 防御:如果 wp-util 被其他插件搞掉了,至少给个能看懂的报错
wp_add_inline_script( 'my-admin-core', '
if ( typeof wp === "undefined" || ! wp.ajax ) {
console.error("[MyPlugin] wp-util not loaded. Check for plugin conflicts.");
}
', 'before' );
另外补一个踩坑细节:我之前试过用 wp_register_script + wp_enqueue_script 分开写,觉得这样"更规范"。结果在 admin_enqueue_scripts 钩子里,另一个同优先级(默认 10)的回调先执行了 wp_enqueue_script('my-admin-core'),但我那时还没 register,直接报 my-admin-core 未注册。后来干脆合并成一步 wp_enqueue_script,或者把注册逻辑挂到更早的 init 钩子,优先级调低(比如 5)。
最后总结下我贴显示器上的检查点,专门对付 wp_enqueue_script 的暗坑:
$deps里写的是"我实际调用的 JS API 所在的脚本",不是"我听说应该依赖的库"- 版本号用
filemtime或构建 hash,杜绝"代码已更新但浏览器不刷新" - 跨脚本调用的全局对象(
wp、jQuery、Vue),用wp_add_inline_script包一层typeof检查 - 多插件共存时,用
wp_script_is( 'handle', 'enqueued' )确认依赖真的在队列里
这仨小时最大的收获:控制台报错的位置和真正出错的位置,中间可能隔了三层依赖和两层缓存。以后看到 jQuery is not defined,我先查 Network 面板的加载顺序,而不是直奔 jQuery CDN 链接。

