mirror of
git://develop.git.wordpress.org/
synced 2025-02-25 09:03:09 +01:00
Script Loader: Move delayed head script to footer when there is a blocking footer dependent.
This prevents a performance regression when a blocking script is enqueued in the footer which depends on a delayed script in the `head` (with `async` or `defer`). In order to preserve the execution order, a delayed dependency must fall back to blocking when there is a blocking dependent. But since it was originally delayed (and thus executes similarly to a footer script), it does not need to be in the head and can be moved to the footer. This prevents blocking the critical rendering path. Props adamsilverstein, westonruter, flixos90. Fixes #59599. See #12009. git-svn-id: https://develop.svn.wordpress.org/trunk@56933 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
cd3e528f93
commit
c2409fe804
@ -231,6 +231,25 @@ class WP_Scripts extends WP_Dependencies {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all dependents of a given handle are in the footer.
|
||||
*
|
||||
* If there are no dependents, this is considered the same as if all dependents were in the footer.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $handle Script handle.
|
||||
* @return bool Whether all dependents are in the footer.
|
||||
*/
|
||||
private function are_all_dependents_in_footer( $handle ) {
|
||||
foreach ( $this->get_dependents( $handle ) as $dep ) {
|
||||
if ( isset( $this->groups[ $dep ] ) && 0 === $this->groups[ $dep ] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a script dependency.
|
||||
*
|
||||
@ -281,6 +300,25 @@ class WP_Scripts extends WP_Dependencies {
|
||||
$intended_strategy = '';
|
||||
}
|
||||
|
||||
/*
|
||||
* Move this script to the footer if:
|
||||
* 1. The script is in the header group.
|
||||
* 2. The current output is the header.
|
||||
* 3. The intended strategy is delayed.
|
||||
* 4. The actual strategy is not delayed.
|
||||
* 5. All dependent scripts are in the footer.
|
||||
*/
|
||||
if (
|
||||
0 === $group &&
|
||||
0 === $this->groups[ $handle ] &&
|
||||
$intended_strategy &&
|
||||
! $this->is_delayed_strategy( $strategy ) &&
|
||||
$this->are_all_dependents_in_footer( $handle )
|
||||
) {
|
||||
$this->in_footer[] = $handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $conditional ) {
|
||||
$cond_before = "<!--[if {$conditional}]>\n";
|
||||
$cond_after = "<![endif]-->\n";
|
||||
|
@ -3065,4 +3065,291 @@ HTML
|
||||
protected function add_html5_script_theme_support() {
|
||||
add_theme_support( 'html5', array( 'script' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a script is moved to the footer if it is made non-deferrable, was in the header and
|
||||
* all scripts that depend on it are in the footer.
|
||||
*
|
||||
* @ticket 58599
|
||||
*
|
||||
* @dataProvider data_provider_script_move_to_footer
|
||||
*
|
||||
* @param callable $set_up Test setup.
|
||||
* @param string $expected_header Expected output for header.
|
||||
* @param string $expected_footer Expected output for footer.
|
||||
* @param string[] $expected_in_footer Handles expected to be in the footer.
|
||||
* @param array $expected_groups Expected groups.
|
||||
*/
|
||||
public function test_wp_scripts_move_to_footer( $set_up, $expected_header, $expected_footer, $expected_in_footer, $expected_groups ) {
|
||||
$set_up();
|
||||
|
||||
// Get the header output.
|
||||
ob_start();
|
||||
wp_scripts()->do_head_items();
|
||||
$header = ob_get_clean();
|
||||
|
||||
// Print a script in the body just to make sure it doesn't cause problems.
|
||||
ob_start();
|
||||
wp_print_scripts( array( 'jquery' ) );
|
||||
ob_end_clean();
|
||||
|
||||
// Get the footer output.
|
||||
ob_start();
|
||||
wp_scripts()->do_footer_items();
|
||||
$footer = ob_get_clean();
|
||||
|
||||
$this->assertEqualMarkup( $expected_header, $header, 'Expected header script markup to match.' );
|
||||
$this->assertEqualMarkup( $expected_footer, $footer, 'Expected footer script markup to match.' );
|
||||
$this->assertEqualSets( $expected_in_footer, wp_scripts()->in_footer, 'Expected to have the same handles for in_footer.' );
|
||||
$this->assertEquals( $expected_groups, wp_scripts()->groups, 'Expected groups to match.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_wp_scripts_move_to_footer.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function data_provider_script_move_to_footer() {
|
||||
return array(
|
||||
'footer-blocking-dependent-of-defer-head-script' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script( 'script-b', 'https://example.com/script-b.js', array( 'script-a' ), null, array( 'in_footer' => true ) );
|
||||
},
|
||||
'expected_header' => '',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-a',
|
||||
'script-b',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 1,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
'footer-blocking-dependent-of-async-head-script' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'async' ) );
|
||||
wp_enqueue_script( 'script-b', 'https://example.com/script-b.js', array( 'script-a' ), null, array( 'in_footer' => true ) );
|
||||
},
|
||||
'expected_header' => '',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" data-wp-strategy="async"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-a',
|
||||
'script-b',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 1,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
'head-blocking-dependent-of-delayed-head-script' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script( 'script-b', 'https://example.com/script-b.js', array( 'script-a' ), null, array( 'in_footer' => false ) );
|
||||
},
|
||||
'expected_header' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js"></script>
|
||||
',
|
||||
'expected_footer' => '',
|
||||
'expected_in_footer' => array(),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 0,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
'delayed-footer-dependent-of-delayed-head-script' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script(
|
||||
'script-b',
|
||||
'https://example.com/script-b.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
},
|
||||
'expected_header' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-b',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 1,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
'delayed-dependent-in-header-and-delayed-dependents-in-footer' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script(
|
||||
'script-b',
|
||||
'https://example.com/script-b.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => false,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'script-c',
|
||||
'https://example.com/script-c.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'script-d',
|
||||
'https://example.com/script-d.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
},
|
||||
'expected_header' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-c.js" id="script-c-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-d.js" id="script-d-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-c',
|
||||
'script-d',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 0,
|
||||
'script-c' => 1,
|
||||
'script-d' => 1,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
'all-dependents-in-footer-with-one-blocking' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script(
|
||||
'script-b',
|
||||
'https://example.com/script-b.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'script-c', 'https://example.com/script-c.js', array( 'script-a' ), null, true );
|
||||
wp_enqueue_script(
|
||||
'script-d',
|
||||
'https://example.com/script-d.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
},
|
||||
'expected_header' => '',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-c.js" id="script-c-js"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-d.js" id="script-d-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-a',
|
||||
'script-b',
|
||||
'script-c',
|
||||
'script-d',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 1,
|
||||
'script-c' => 1,
|
||||
'script-d' => 1,
|
||||
'jquery' => 0,
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
'blocking-dependents-in-head-and-footer' => array(
|
||||
'set_up' => static function () {
|
||||
wp_enqueue_script( 'script-a', 'https://example.com/script-a.js', array(), null, array( 'strategy' => 'defer' ) );
|
||||
wp_enqueue_script(
|
||||
'script-b',
|
||||
'https://example.com/script-b.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => false,
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'script-c', 'https://example.com/script-c.js', array( 'script-a' ), null, true );
|
||||
wp_enqueue_script(
|
||||
'script-d',
|
||||
'https://example.com/script-d.js',
|
||||
array( 'script-a' ),
|
||||
null,
|
||||
array(
|
||||
'strategy' => 'defer',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
},
|
||||
'expected_header' => '
|
||||
<script type="text/javascript" src="https://example.com/script-a.js" id="script-a-js" data-wp-strategy="defer"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-b.js" id="script-b-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_footer' => '
|
||||
<script type="text/javascript" src="https://example.com/script-c.js" id="script-c-js"></script>
|
||||
<script type="text/javascript" src="https://example.com/script-d.js" id="script-d-js" defer="defer" data-wp-strategy="defer"></script>
|
||||
',
|
||||
'expected_in_footer' => array(
|
||||
'script-c',
|
||||
'script-d',
|
||||
),
|
||||
'expected_groups' => array(
|
||||
'script-a' => 0,
|
||||
'script-b' => 0,
|
||||
'script-c' => 1,
|
||||
'script-d' => 1,
|
||||
'jquery' => 0,
|
||||
),
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user