jQuery 是一个很大的库,如果能从 CDN 加载,是最好不过的了,这就涉及到要替换 jQuery 的地址,而且我准备前台后台都要替换。
WordPress 作为一个插件系统极其变态的存在,自然是有办法替换的了。现在网上的很多现成代码都是两三年前的了,也就意味着,没用,那就自己写吧。
下面是写出来始终没法正常工作的第一版:
// This works bad with concat
add_action( 'init', 'script_movesrc2lfs');
function script_movesrc2lfs(){
$path = 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js';
$e = $o->query('jquery', 'registered');
if($e && $e->ver == '1.12.3'){
$e = $o->query( 'jquery-core', 'registered');
$e->src = $path;
}
}
直接在 registered 层面就把地址替换了,原理看起来是很简单的。
然而,因为 WordPress 对后台所有的 scripts 都会做拼接处理(concat),每次 jQuery 应该是第一个拼接的。经过这段代码之后,jQuery 库的地址不在本地,WP 就把这个库移到拼接 script 的后面了。后面了。后面了。
后面啊!别的依赖它的程序还怎么运行啊!
另外,之前还想把 migrate 拿掉,结果天啊,万万没想到破 Akismet 的 Javascript 还依赖 migrate。
于是这是还算能用的第二版:
add_action( 'wp_enqueue_scripts', 'script_movesrc2lfs', 5);
add_action( 'admin_print_scripts', 'script_movesrc2lfs', 5);
function script_movesrc2lfs(){
$o = wp_scripts();
$path = 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js';
$ver = null;
$e = $o->query('jquery', 'registered');
if($e && $e->ver == '1.12.3'){ // Remove $e->ver if you want to be aggressive
$e->deps = array('jquery-migrate');
// Removed jquery-core
$e = $o->query( 'jquery-core', 'registered');
$ver = $e->ver;
$e->src = $path;
}
$e = $o->query('jquery', 'queue');
if($e && $e->ver == '1.12.3'){ // Remove $e->ver if you want to be aggressive
echo "<script type='text/javascript' src='".$path."?ver=".$ver."'></script>n";
}
}
这块的逻辑是,自己实现 header 位置 jquery 依赖 jquery-core 的过程,因为在 print_head_scripts 前执行,只要再把 jquery-core 从 queue 列表中拿走,就不会影响到自动 concat 的过程了。
带来的可能存在的问题是,如果程序是在 footer 请求的 jquery,就只能请求到 jquery-migrate,core 加载不进来。如果直接请求 jquery-core 是不受影响的。因为我自己的 WordPress 不会有这种情况,就把逻辑省下来了。插入回去的话还得特意再插个 action 自己实现 footer 的依赖。
写完上面这段,出去吃了个饭,觉得有点不甘心。我们的思路已经很清楚了,就是要自己实现 deps,那怎么不直接 Hook 到处理 deps 的位置呢?吃完饭回来查了下,果然 WordPress 在这么细节的位置也有 Hook(print_scripts_array),那就出第三版咯。
add_action( 'wp_print_scripts', 'script_movesrc2lfs', 5);
add_filter( 'print_scripts_array', 'script_movesrc2lfs_handledeps', 5);
function script_movesrc2lfs(){
$o = wp_scripts();
$path = 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js';
$ver = '1.12.3'; // To be more aggressive, use null
$e = $o->query('jquery', 'registered');
if($e && (!$ver || $e->ver == $ver)){
// $e->deps = array('jquery-core');
// Akismet 居然还依赖 Migrate,无法去除
$e = $o->query( 'jquery-core', 'registered');
$e->src = $path;
}
}
function script_movesrc2lfs_handledeps($array){
$o = wp_scripts();
if($o->do_concat && in_array('jquery', $array)){
// Remove it from the array
$array = array_diff($array, array('jquery-core'));
// Handle dependencies by ourselves
$o->do_concat = false;
$o->do_item('jquery-core');
$o->do_concat = true;
}
return $array;
}
完美。如果要替换更多的库文件也是同理,就是要注意自己实现依赖的时候,前后的顺序咯。
还有,WordPress 自己用的 jQuery 文件后面有带 noConflict(),换了 CDN 一般就没这句了。因为我觉得让 jQuery 污染了 $ 之后好像没什么问题,就没在 inline 代码里补上。如果要彻底模拟 WP 的功能的话还是应该加上的。
后话:noConflict 是为了跟 Prototype 兼容的,然而 WP 早就不自带 Prototype 了(代码直接引用的 Google CDN)。
P.S. 截至发稿时,Google CDN 还没有同步 jQuery 1.12.3 版,所以上面代码直接运行是运行不动的。按照国情,我实际用的 CDN 地址自然是自己的 CDN 咯。