`wp_enqueue_script` 传了 `$in_footer = true` 却仍在 `<head>` 冒头:我查清 `wp_register_script` 与 `wp_enqueue_script` 参数"打架"的全经过
上周给插件加前端交互,明明在 wp_enqueue_script 里写了 true 塞底部,结果 F12 一看脚本还在 <head> 里躺着,挡了首屏渲染。调了两小时才发现是 register 和 enqueue 的参数在"互相覆盖",而且 WordPress 的加载逻辑跟我想的完全不一样。
先贴我最初的错误写法,估计不少人踩过:
// 错误示范:register 时已经定了位置,enqueue 再传一次,结果不可预期
wp_register_script(
'my-plugin-chart',
plugin_dir_url( __FILE__ ) . 'assets/chart.js',
['jquery'],
'1.2.0',
false // ← 这里写了 false,要放头部
);
add_action( 'wp_enqueue_scripts', function() {
wp_enqueue_script(
'my-plugin-chart',
'', // src 留空,用 register 的
[], // deps 留空
false, // ver 留空
true // ← 这里又写了 true,想塞底部
);
});
看起来 enqueue 里传了 true 应该能覆盖吧?实际 WordPress 的处理逻辑是:如果 enqueue 的 $src 为空字符串,它会直接沿用 register 时存的全部参数,你后面传的 $in_footer 直接被忽略。源码在 wp-includes/class.wp-scripts.php 的 add() 方法里,空 src 时不会触发 data['group'] 的重新计算。
更坑的是,如果你 register 时没传版本号、enqueue 时传了个布尔值当版本,还会触发类型错位:
// 错上加错:true 被当成版本号字符串 "1",$in_footer 用了默认值 false
wp_enqueue_script( 'my-plugin-chart', '', [], true, true );
// 实际效果:ver = "1"(非预期),in_footer = false(非预期)
正确的几种写法,按场景选:
场景 A:注册和加载分开,参数全在 register 里定死
wp_register_script(
'my-plugin-chart',
plugin_dir_url( __FILE__ ) . 'assets/chart.js',
['jquery'],
'1.2.0',
true // ← 底部加载,一次定终身
);
add_action( 'wp_enqueue_scripts', function() {
wp_enqueue_script( 'my-plugin-chart' ); // 什么都不传,纯唤起
});
场景 B:不预注册,直接 enqueue 一次性搞定(推荐简单脚本)
add_action( 'wp_enqueue_scripts', function() {
wp_enqueue_script(
'my-plugin-chart',
plugin_dir_url( __FILE__ ) . 'assets/chart.js',
['jquery'],
'1.2.0',
true // ← 只有这里出现一次
);
});
场景 C:必须动态决定位置(比如某些页面要头部、某些要底部)
// 注册时不定位置(或按默认走)
wp_register_script(
'my-plugin-chart',
plugin_dir_url( __FILE__ ) . 'assets/chart.js',
['jquery'],
'1.2.0'
);
add_action( 'wp_enqueue_scripts', function() {
$in_footer = is_singular( 'product' ); // 商品页塞底部
// 关键:src 不能留空,必须重新完整声明才能覆盖 group
wp_enqueue_script(
'my-plugin-chart',
plugin_dir_url( __FILE__ ) . 'assets/chart.js',
['jquery'],
'1.2.0',
$in_footer
);
});
注意场景 C 的代价:每次 enqueue 都要重新拼 URL,失去了预注册的性能优势。如果高频调用,建议拆成两个 handle:
wp_register_script( 'my-chart-head', ..., false );
wp_register_script( 'my-chart-foot', ..., true );
// 按需唤起,零参数冲突
is_singular( 'product' )
? wp_enqueue_script( 'my-chart-foot' )
: wp_enqueue_script( 'my-chart-head' );
最后补个排错命令,下次怀疑脚本位置时直接打日志:
add_action( 'wp_print_scripts', function() {
global $wp_scripts;
foreach ( $wp_scripts->queue as $handle ) {
$script = $wp_scripts->registered[$handle];
error_log( sprintf(
'[脚本位置] %s: group=%d (0=head, 1=footer), src=%s',
$handle,
$script->args, // 这个字段存的就是 group 值
$script->src
));
}
}, 100 );
输出 group=0 就是头部,group=1 才是底部。别跟我一样对着 $in_footer 参数干瞪眼了。

