WordPress主题开发

WordPress优化CSS和JS的加载(2021)

通常情况下,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_footerstrategy,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会被覆盖掉,变成默认的阻塞加载行为。

1条评论

评论已关闭。