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.


git-svn-id: https://develop.svn.wordpress.org/trunk@55847 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Felix Arntz
2023-05-22 19:11:36 +00:00
parent 3c6184d81c
commit 71140f327f
4 changed files with 279 additions and 20 deletions

View File

@@ -897,4 +897,71 @@ class Tests_Query extends WP_UnitTestCase {
$this->assertFalse( $q->is_tax() );
$this->assertFalse( $q->is_tag( 'non-existent-tag' ) );
}
/**
* Test if $before_loop is true before loop.
*
* @ticket 58211
*/
public function test_before_loop_value_set_true_before_the_loop() {
// Get a new query with 3 posts.
$query = $this->get_new_wp_query_with_posts( 3 );
$this->assertTrue( $query->before_loop );
}
/**
* Test $before_loop value is set to false when the loop starts.
*
* @ticket 58211
*
* @covers WP_Query::the_post
*/
public function test_before_loop_value_set_to_false_in_loop_with_post() {
// Get a new query with 2 posts.
$query = $this->get_new_wp_query_with_posts( 2 );
while ( $query->have_posts() ) {
// $before_loop should be set false as soon as the_post is called for the first time.
$query->the_post();
$this->assertFalse( $query->before_loop );
break;
}
}
/**
* Test $before_loop value is set to false when there is no post in the loop.
*
* @ticket 58211
*
* @covers WP_Query::have_posts
*/
public function test_before_loop_set_false_after_loop_with_no_post() {
// New query without any posts in the result.
$query = new WP_Query(
array(
'category_name' => 'non-existent-category',
)
);
// There will not be any posts, so the loop will never actually enter.
while ( $query->have_posts() ) {
$query->the_post();
}
// Still, this should be false as there are no results and entering the loop was attempted.
$this->assertFalse( $query->before_loop );
}
/**
* Get a new query with a given number of posts.
*
* @param int $no_of_posts Number of posts to be added in the query.
*/
public function get_new_wp_query_with_posts( $no_of_posts ) {
$post_ids = self::factory()->post->create_many( $no_of_posts );
$query = new WP_Query( array( 'post__in' => $post_ids ) );
return $query;
}
}