Media: Conditionally skip lazy-loading on images before the loop to improve LCP performance.

When the logic to exclude images that likely appear above the fold from being lazy-loaded was introduced in WordPress 5.9, initially only images that appear within the main query loop were being considered. However, there is a good chance that images above the fold are rendered before the loop starts, for example in the header template part.

It is particularly common for a theme to display the featured image for a single post in the header. Based on HTTP Archive data from February 2023, the majority of LCP images that are still being lazy-loaded on WordPress sites use the `wp-post-image` class, i.e. are featured images.

This changeset enhances the logic in `wp_get_loading_attr_default()` to not lazy-load images that appear within or after the header template part and before the query loop, using a new `WP_Query::$before_loop` property.

For block themes, this was for the most part already addressed in [55318], however this enhancement implements the solution in a more generally applicable way that brings the improvement to classic themes as well.

Props thekt12, flixos90, spacedmonkey, costdev, zunaid321, mukesh27.
Fixes #58211.
See #53675, #56930.

Built from https://develop.svn.wordpress.org/trunk@55847


git-svn-id: http://core.svn.wordpress.org/trunk@55359 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Felix Arntz 2023-05-22 19:13:21 +00:00
parent e182459c6d
commit c6f9238b77
3 changed files with 43 additions and 9 deletions

View File

@ -108,6 +108,14 @@ class WP_Query {
*/ */
public $current_post = -1; public $current_post = -1;
/**
* Whether the caller is before the loop.
*
* @since 6.3.0
* @var bool
*/
public $before_loop = true;
/** /**
* Whether the loop has started and the caller is in the loop. * Whether the loop has started and the caller is in the loop.
* *
@ -517,6 +525,7 @@ class WP_Query {
$this->post_count = 0; $this->post_count = 0;
$this->current_post = -1; $this->current_post = -1;
$this->in_the_loop = false; $this->in_the_loop = false;
$this->before_loop = true;
unset( $this->request ); unset( $this->request );
unset( $this->post ); unset( $this->post );
unset( $this->comments ); unset( $this->comments );
@ -3631,6 +3640,7 @@ class WP_Query {
} }
$this->in_the_loop = true; $this->in_the_loop = true;
$this->before_loop = false;
if ( -1 == $this->current_post ) { // Loop has just started. if ( -1 == $this->current_post ) { // Loop has just started.
/** /**
@ -3671,6 +3681,8 @@ class WP_Query {
// Do some cleaning up after the loop. // Do some cleaning up after the loop.
$this->rewind_posts(); $this->rewind_posts();
} elseif ( 0 === $this->post_count ) { } elseif ( 0 === $this->post_count ) {
$this->before_loop = false;
/** /**
* Fires if no results are found in a post query. * Fires if no results are found in a post query.
* *

View File

@ -5490,32 +5490,54 @@ function wp_get_webp_info( $filename ) {
* *
* @since 5.9.0 * @since 5.9.0
* *
* @global WP_Query $wp_query WordPress Query object.
*
* @param string $context Context for the element for which the `loading` attribute value is requested. * @param string $context Context for the element for which the `loading` attribute value is requested.
* @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate * @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
* that the `loading` attribute should be skipped. * that the `loading` attribute should be skipped.
*/ */
function wp_get_loading_attr_default( $context ) { function wp_get_loading_attr_default( $context ) {
global $wp_query;
// Skip lazy-loading for the overall block template, as it is handled more granularly. // Skip lazy-loading for the overall block template, as it is handled more granularly.
if ( 'template' === $context ) { if ( 'template' === $context ) {
return false; return false;
} }
// Do not lazy-load images in the header block template part, as they are likely above the fold. // Do not lazy-load images in the header block template part, as they are likely above the fold.
// For classic themes, this is handled in the condition below using the 'get_header' action.
$header_area = WP_TEMPLATE_PART_AREA_HEADER; $header_area = WP_TEMPLATE_PART_AREA_HEADER;
if ( "template_part_{$header_area}" === $context ) { if ( "template_part_{$header_area}" === $context ) {
return false; return false;
} }
// Special handling for programmatically created image tags.
if ( ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) ) {
/* /*
* Skip programmatically created images within post content as they need to be handled together with the other * Skip programmatically created images within post content as they need to be handled together with the other
* images within the post content. * images within the post content.
* Without this clause, they would already be counted below which skews the number and can result in the first * Without this clause, they would already be counted below which skews the number and can result in the first
* post content image being lazy-loaded only because there are images elsewhere in the post content. * post content image being lazy-loaded only because there are images elsewhere in the post content.
*/ */
if ( ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) && doing_filter( 'the_content' ) ) { if ( doing_filter( 'the_content' ) ) {
return false; return false;
} }
// Conditionally skip lazy-loading on images before the loop.
if (
// Only apply for main query but before the loop.
$wp_query->before_loop && $wp_query->is_main_query()
/*
* Any image before the loop, but after the header has started should not be lazy-loaded,
* except when the footer has already started which can happen when the current template
* does not include any loop.
*/
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
) {
return false;
}
}
/* /*
* The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded, * The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
* as they are likely above the fold. * as they are likely above the fold.

View File

@ -16,7 +16,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '6.3-alpha-55846'; $wp_version = '6.3-alpha-55847';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.