[wp-trac] [WordPress Trac] #61734: Add the ability to handle "fetchpriority" to ES Modules and Import Maps

WordPress Trac noreply at wordpress.org
Wed Jul 31 23:07:38 UTC 2024


#61734: Add the ability to handle "fetchpriority" to ES Modules and Import Maps
---------------------------+--------------------------------------
 Reporter:  dennysdionigi  |       Owner:  westonruter
     Type:  enhancement    |      Status:  accepted
 Priority:  normal         |   Milestone:  6.7
Component:  Script Loader  |     Version:  6.5
 Severity:  minor          |  Resolution:
 Keywords:  needs-patch    |     Focuses:  javascript, performance
---------------------------+--------------------------------------
Changes (by westonruter):

 * keywords:   => needs-patch
 * owner:  (none) => westonruter
 * version:  6.6.1 => 6.5
 * status:  new => accepted
 * milestone:  Awaiting Review => 6.7


Comment:

 I do indeed see that on a page with the Interactivity API present for the
 Image block and the Navigation block, that these scripts are all loaded
 with '''high priority''' in Chrome:

 * `/wp-includes/blocks/navigation/view.js`
 * `/wp-includes/blocks/image/view.js`
 * `/wp-includes/js/dist/interactivity.js`

 The HTML in question:

 {{{
 <script type="importmap" id="wp-importmap">
 {"imports":{"@wordpress\/interactivity":"http:\/\/localhost:8888\/wp-
 includes\/js\/dist\/interactivity.js?ver=6.7-alpha-58835"}}
 </script>
 <script type="module" src="http://localhost:8888/wp-
 includes/blocks/navigation/view.js?ver=6.7-alpha-58835" id="@wordpress
 /block-library/navigation-js-module"></script>
 <script type="module" src="http://localhost:8888/wp-
 includes/blocks/image/view.js?ver=6.7-alpha-58835" id="@wordpress/block-
 library/image-js-module"></script>
 <link rel="modulepreload" href="http://localhost:8888/wp-
 includes/js/dist/interactivity.js?ver=6.7-alpha-58835" id="@wordpress
 /interactivity-js-modulepreload">
 }}}

 This doesn't seem ideal, considering that server-side rendering should be
 employed by interactive blocks, meaning that the scripting should not be
 in the critical path for rendering the page. By loading these scripts with
 high priority, they may be hurting LCP by adding network contention for
 fetching the LCP image.

 For adding `fetchpriority` for a script module, either this could be added
 to the `script` tag itself or it could be added as a
 `link[rel=modulepreload]` tag that the same `src`.

 For traditional scripts and module scripts alike, you can use the
 `wp_script_attributes` filter to inject the `fetchpriority` attribute onto
 the `script` tag. For example, to mark all module scripts to load with
 `fetchpriority=low`:

 {{{#!php
 <?php
 add_filter(
         'wp_script_attributes',
         static function ( $attributes ) {
                 if (
                         isset( $attributes['type'], $attributes['id'] ) &&
                         'module' === $attributes['type'] &&
                         str_starts_with( $attributes['id'], '@wordpress
 /block-library/' )
                 ) {
                         $attributes['fetchpriority'] = 'low';
                 }
                 return $attributes;
         }
 );
 }}}

 However, this does not help when a script module is not directly enqueued
 in which case a `link[rel=modulepreload]` is added via
 `WP_Script_Modules::print_script_module_preloads()` which normally occurs
 for the `@wordpress/interactivity` script. And there is no way to insert
 `fetchpriority=low` into the generated `link` tag without some hacking:

 {{{#!php
 <?php
 add_action(
         'init',
         static function () {
                 $position = wp_is_block_theme() ? 'wp_head' : 'wp_footer';
                 $priority = has_action( $position, array(
 wp_script_modules(), 'print_script_module_preloads' ) );
                 if ( false === $priority ) {
                         return;
                 }
                 remove_action( $position, array( wp_script_modules(),
 'print_script_module_preloads' ), $priority );
                 add_action(
                         $position,
                         static function () {
                                 ob_start();
 wp_script_modules()->print_script_module_preloads();
                                 $buffer = ob_get_clean();
                                 if ( '' === $buffer ) {
                                         return;
                                 }
                                 $processor = new WP_HTML_Tag_Processor(
 $buffer );
                                 while ( $processor->next_tag( array(
 'tag_name' => 'LINK' ) ) ) {
                                         $processor->set_attribute(
 'fetchpriority', 'low' );
                                 }
                                 echo $processor->get_updated_html();
                         },
                         $priority
                 );
         }
 );
 }}}


 I put these snippets in a plugin you can use for testing as well:
 https://gist.github.com/westonruter/471111a891f43e0f48bc7e0ca478623d

 Given a test post using the TT4 theme that contains one lightbox-enabled
 Image block which is the LCP element, where the responsive Navigation
 block is also present:

 I tried [https://github.com/GoogleChromeLabs/wpp-research/tree/main/cli
 #benchmark-web-vitals benchmarking] page loads with the existing behavior
 where all scripts are loaded with high priority, and over 100 requests the
 median LCP-TTFB was 48.15 ms.

 Then I tried adding `fetchpriority=low` to the scripts, and over 100
 requests the median LCP-TTFB was 44.6 ms.

 '''So adding `fetchpriority=low` makes the LCP metric here 7.37%
 faster.'''


 (The TTFB-LCP metric here is the LCP discounting variations in TTFB.)


 I think the Interactivity API should add `fetchpriority=low` to all of the
 module scripts it registers.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/61734#comment:2>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list