通常情况下,css和js会阻碍html的解析,在谷歌测速里会被当成阻碍渲染的资源。如何优化呢?答案很简单,给javascript文件添加defer标签,异步加载。css文件,可以用preload方式加载,也可以用一点小技巧使其异步加载。本文简单介绍如何优化CSS和JS的加载。
目录
根据$handle选择异步加载的脚本
function sola_defer_scripts( $tag, $handle, $src ) {
if( is_admin() || $GLOBALS['pagenow'] == 'wp-login.php'){
return $tag;
}
$non_defer = array(
'jquery-core',
);
if ( in_array( $handle, $non_defer ) || strpos($tag, 'defer') !== false ) {
return $tag;
}
// Defer everything else
return '<script src="' . $src . '" type="text/javascript" defer></script>' . "\n";
}
add_filter('script_loader_tag', 'sola_defer_scripts', 10, 3);
根据文件的url选择异步加载的脚本
这段代码把除了jquery之外的脚本都defer掉。
function sola_defer_scripts( $tag, $handle, $src ) {
if( is_admin() || $GLOBALS['pagenow'] == 'wp-login.php'){
return $tag;
}
// Do not defer jquery
if ( strpos( $src, 'jquery.min.js' ) ){
return $tag;
}
// Defer everything else
return str_replace( ' src', ' defer src', $tag );
}
add_filter( 'script_loader_tag', 'sola_defer_scripts', 10, 3 );
为什么defer js文件后网站报JavaScript错误
情况一:jQuery没有defer,其它脚本defer,网站报错。
一种概率比较大的可能是:并非所有脚本都被defer了。理论上jQuery正常加载,其它依赖或不依赖jQuery的脚本defer加载,defer之前不报错,defer之后也不该报错。意外情况是有些依赖jQuery的脚本没defer,那当然会出错。
什么情况下会出现部分defer? sola在使用Siteground Optimizer时发现了这种情况。使用优化插件的defer功能时,要自己检查所有带有src的script标签是否都有defer属性。
情况二:包括jQuery在内的所有脚本都defer。
jQuery也不是不能defer,你要确保所有调用jQuery的脚本都defer加载,并且inline JavaScript不直接调用jQuery。
如果你在inline JavaScript中直接写了jQuery代码,而jQuery 是defer加载,就会报错。因为inline JavaScript默认不能defer,那jQuery还没加载它就运行了,肯定不行的。一种解决方案是将jQuery Ready事件放到DOMContentLoaded事件上执行。
<script>
window.addEventListener('DOMContentLoaded', function(){
if ( typeof(jQuery) !== 'undefined' ){
jQuery(document).ready(function($) {
// Your jQuery code
});
}
});
</script>
关于这个问题更详细的讨论,请参考How to defer inline Javascript?
Preload或者异步加载CSS
CSS preload会让浏览器以最高优先级下载文件,并且不再阻碍html解析。下面的代码演示了两种异步加载css的方法,一种是Scott Jehl在文章The Simplest Way to Load CSS Asynchronously介绍的方法,另一种是css preload,根据自己的需要选择即可。
function sola_defer_styles( $tag, $handle, $href, $media ){
if( is_admin() || $GLOBALS['pagenow'] == 'wp-login.php'){
return $tag;
}
$not_to_defer = array(
'theme-main',
);
if ( ! in_array( $handle, $not_to_defer ) ) {
return '<link rel="stylesheet" href="' . $href . '" media="print" onload="this.media=\'all\'">' . "\n" .
'<noscript><link rel="stylesheet" href="' . $href . '" media="all"></noscript>' . "\n";
} else {
// Preload css
$tag .= '<link rel="preload" href="' .$href. '" as="style">';
}
return $tag;
}
add_filter( 'style_loader_tag', 'sola_defer_styles',10, 4);
Inline CSS
渲染页面顶部的样式或者只用在某些页面的样式可以内联加载,可以用wp_head
hook或wp_print_styles
hook来打印样式。使用时要注意优先级,如下所示。
add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
还有一个函数叫wp_add_inline_style(),通常它要和一个样式表关联,但也可以仅用来输出内联样式,方法如下:
add_action( 'wp_enqueue_scripts', 'sola_inline_styles' );
function sola_inline_styles(){
$styles = 'body{background:#000}';
$handle = 'my-custom-style';
wp_register_style( $handle, false );
wp_add_inline_style( $handle, $styles );
wp_enqueue_style( $handle );
}
如何查看网站加载了哪些脚本
Sola喜欢用Query Monitor插件查看,也可以用代码,点击参考文章里的链接就有。
参考文章:
WordPress 6.3的新特性
WordPress 6.3可以延迟加载或异步加载scripts,对wp_register_script()
和wp_enqueue_script()
的功能进行了扩充,将原来布尔类型的in_footer
参数,也就是第四个参数改成了数组,可以传递in_footer
或strategy
,stragety的值可以是async或defer,从而决定脚本的加载方式。根据官方说法,WordPress 6.3在客户端性能方面作了很多改进,从而提升LCP(最大内容绘制)的响应速度。
此次更改是扩展功能,以前的代码如果使用了第四个参数,现在也能正常工作。但新代码在旧版WordPress下的行为却略有不同,给一个bool型变量传递有内容的数组,会被转换为bool true,会使脚本在footer加载,这对性能是有好处的,所以问题不大。下面来看看新版中如何加载脚本。
注册脚本时声明defer
wp_register_script(
'foo',
'/path/to/foo.js',
array(),
'1.0.0',
array(
'strategy' => 'defer'
)
);
异步加载,且在footer打印该script标签
wp_register_script(
'bar',
'/path/to/bar.js',
array(),
'1.0.0',
array(
'in_footer' => true,
'strategy' => 'async',
)
)
兼容6.3之前版本的写法
wp_register_script(
'foo',
'/path/to/foo.js',
array(),
'1.0.0',
false
);
wp_script_add_data( 'foo', 'strategy', 'defer' );
内联脚本可能会更改脚本加载行为,如果使用wp_add_inline_script()
函数插入了inline script,且strategy设定为async或defer,为了保证所以关联脚本的正确运行,async或defer会被覆盖掉,变成默认的阻塞加载行为。
马克,学习了。