wp_enqueue_script 的依赖顺序:为什么你的插件总说 $ 未定义

· wordpress · 阅读 16
摘要

队列机制与依赖数组、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

参数输出位置适合
truewp_footer99% 的插件脚本、要操作 DOM 的、统计代码
falsewp_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 defineddeps 没写 jquery,或脚本在 head 而 jquery 在 footer
jQuery 明明有报 $ is not definednoConflict,需 IIFE 包一层
脚本完全没输出钩子用错(前台用了 admin_),或条件判断挡掉了
改文件不生效version 参数没变,浏览器用缓存;或页面缓存插件没清

改 version 这条特别常见:wp_enqueue_script 的第四个参数会拼进 ?ver=,你不改它,浏览器永远拿旧的。开发时用 filemtime($path) 自动当版本号,能省掉一半「改了没反应」的困惑。

小结

三个动作:声明 deps(把 jquery 写进依赖数组)、IIFE 包 $(绕开 noConflict)、放 footer(true 第五个参数)。这三条覆盖了 $ is not defined 的九成场景。