wp_enqueue_script 的依赖顺序:为什么你的插件总说 $ 未定义
队列机制与依赖数组、noConflict 下 $ 的两种解法、head 与 footer 的取舍,附症状对照自查表。
$ is not defined 是 WordPress 前端最常见的一条报错,绝大多数不是 jQuery 没加载,而是加载顺序错了。这个顺序不靠运气,靠你在 wp_enqueue_script 第三个参数里写的依赖数组。
一、队列机制:先登记,后输出
WordPress 不是调用就立刻输出 <script>,而是先往队列里登记,等 wp_head 或 wp_footer 时统一解析依赖关系再按拓扑顺序输出。所以:
- 你可以在
init甚至更早注册脚本 - 实际输出位置由
$in_footer参数决定 - 依赖声明得对,顺序才自动正确
二、正确写法
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script(
'my-plugin', // handle
plugins_url('js/my.js', __FILE__), // src
array('jquery'), // deps
'1.0.0', // version
true // in_footer
);
});
关键就在 array('jquery')。它告诉 WordPress:跑我这个脚本之前,jQuery 必须已经在页面上。WordPress 会自动把 jquery 插到队列里(如果还没人加过),并保证顺序。
不写 deps 会怎样:你的脚本可能先于 jquery 输出,浏览器执行到 $(...) 时 jQuery 还不存在,直接抛错。本地网络快的时候偶尔能蒙对,用户网络一慢就全线崩。
三、jQuery 的两个坑
坑一:$(...) 在 WP 里也报错
WordPress 自带的 jQuery 跑在 noConflict 模式下,全局 $ 被让出(留给别的库)。所以即使顺序正确,你在插件 JS 里直接写 $(document) 仍会报 $ is not defined。
两种解法:
// 解法 A:用全名
jQuery(document).ready(function ($) {
// 这里可以用 $
});
// 解法 B:IIFE 包一层
(function ($) {
$(function () {
// 这里可以用 $
});
})(jQuery);
解法 B 更常用。注意:这只是把 $ 变成本地变量,不影响其它插件,比自己 window.$ = jQuery 抢回全局安全得多(后者会搞坏别人的代码)。
坑二:主题里 jQuery 被 deregister
有些主题为了提速会 wp_deregister_script('jquery') 然后自己加载别的版本。这时候你依赖 jquery 也没用——它被注销了,WordPress 不会去加载。
排查:wp_script_is('jquery', 'registered') 返回 false 就说明被注销了。应对是自带一份并换个 handle,或者明确要求主题别动它。
四、CSS 也得管顺序
wp_enqueue_style 同样有依赖参数:
wp_enqueue_style('my-base', $base_css, array(), '1.0');
wp_enqueue_style('my-theme', $theme_css, array('my-base'), '1.0');
子主题覆盖父主题样式时,把父主题的 handle 写进 deps,或者干脆用更高优先级挂 wp_enqueue_scripts(注意是优先级 20 而不是 10),让子主题的 CSS 后入队。
五、在 footer 还是 head
| 参数 | 输出位置 | 适合 |
|---|---|---|
true | wp_footer | 99% 的插件脚本、要操作 DOM 的、统计代码 |
false | wp_head | 必须在渲染前跑的,比如某些 polyfill |
默认是 false(head),但几乎所有插件都应该显式传 true。放 footer 不阻塞首屏渲染,且 DOM 已就绪,不用等 DOMContentLoaded。
但依赖跨区会出问题:如果 jquery 在 head、你的脚本在 footer,顺序天然正确。反过来——你的脚本声明在 head 却依赖 footer 里的脚本——WordPress 会把被依赖者往前提到 head,可能导致别的意外。统一放 footer 最省心。
六、只在需要时加载
全站加载插件脚本很浪费。wp_enqueue_scripts 里做条件判断:
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
wp_enqueue_script('my-single', $url, array('jquery'), '1.0', true);
});
后台脚本走 admin_enqueue_scripts,别混用——在后台钩子里挂前台脚本,或者反过来,都会静默失效。
七、快速自查清单
| 症状 | 原因 |
|---|---|
刚进页面就 $ is not defined | deps 没写 jquery,或脚本在 head 而 jquery 在 footer |
jQuery 明明有报 $ is not defined | noConflict,需 IIFE 包一层 |
| 脚本完全没输出 | 钩子用错(前台用了 admin_),或条件判断挡掉了 |
| 改文件不生效 | version 参数没变,浏览器用缓存;或页面缓存插件没清 |
改 version 这条特别常见:wp_enqueue_script 的第四个参数会拼进 ?ver=,你不改它,浏览器永远拿旧的。开发时用 filemtime($path) 自动当版本号,能省掉一半「改了没反应」的困惑。
小结
三个动作:声明 deps(把 jquery 写进依赖数组)、IIFE 包 $(绕开 noConflict)、放 footer(true 第五个参数)。这三条覆盖了 $ is not defined 的九成场景。