Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use defer loading strategy for frontend view scripts #52536

Merged
merged 28 commits into from
Jul 25, 2023

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Jul 11, 2023

What?

Use the defer and async loading strategies for frontend view scripts, which WordPress 6.3 is introducing in Core-12009. See Dev Note.

If the defer loading strategy were used exclusively, then none of the changes to the JS files would have been necessary. However, to further improve user experience with some blocks, the use of the async loading strategy was used for the Navigation and Search blocks so that their view scripts can load in the head and execute as soon as possible so that the user can click on these blocks which are normally in the header. This required a refactor to employ event delegation instead of the DOMContentLoaded or load events.

Why?

Fixes #51930.

These can now be leveraged to improve loading performance for frontend scripts by not blocking the rendering of the page. It also improves UX by allowing behaviors for UI elements in the Navigation block and Search block to be attached before the DOM has fully loaded.

How?

Add the strategy script data with the value of async or defer for frontend scripts.

  1. For comment-reply, it sets the defer loading strategy which we'll likely eventually do in core with v6.4. This change is done for the Comments block and the Post Comments Form block. This provides a way to test deferred loading in the meantime. In practice, this change will have minimal effect since it is already printed in the footer.
  2. All Interactivity API scripts are marked as defer. This addresses a todo to leverage script deferring when it is in core. This change also will have minimal impact because these scripts are also printed in the footer. (The experimental Interactivity API is enabled by default when using the Gutenberg plugin, but can be disabled via filter in Add filter to turn off Interactivity API for a block #52579.)
  3. The async and defer loading strategies are added manually via wp_script_add_data() as outlined in the Dev Note. Ideally the loading strategy and whether it can load in the footer could be indicated in the block.json. See Blocks: Allow customizations for assets in block.json #46954 and Core-54018.
  4. Block view scripts that don't use the Interactivity API are modified as follows:

Navigation Block

Fixes #42394.

Loading strategy: defer async

  • Only enqueue the view.js script and view-modal.js script if submenu-on-click and overlay menu (responsive mobile menu button) options are enabled, respectively. Previously both of these scripts would be enqueued if either options were enabled. This ensures either script is only enqueued if it is actually needed.
  • Use delegation to listen for click events anywhere in the document rather than waiting to attach click events on all elements at the window's load event. The load event is particularly problematic because it only fires after all page resources have loaded, including images, fonts, styles, and scripts. Presumably DOMContentLoaded would have been a better fit. In any case, event delegation is even better than using either of these events because it allows for the view scripts to be loaded asynchronously in the head (the async loading strategy) and not wait for the page to finish loading. This means that even when the page HTML has half-loaded, if the async view scripts loaded from the head have executed, the user can much sooner start interacting with the navigation block and not rage click on buttons without anything happening.
  • Only attach event listeners for links in an opened model when the modal is actually opened.
  • Use passive event listeners to marginally improve UI performance.
  • Behavior of the improved implementations of the Navigation block's view scripts are likely more performant and resilient than the current Interactivity API implementation due to the asynchronous loading.

Search Block

Loading strategy: defer async

  • Register view script in block.json and conditionally include/exclude based on whether block options require it. (This follows the same pattern from the Navigation block and File block.) This was done independently as of Search block: Enqueue view script through block.json #52552.
  • Event delegation is employed the same as in the Navigation block. No DOMContentLoaded event is used, so when the script is asynchronously loaded from the head the user can tap on a collapsed Search block as before the DOM fully loads.
  • Only attach event listeners for a Search block when it is expanded.
  • Use passive event listeners to marginally improve UI performance.
  • Collapse an expanded Search block when tabbing out of the form. Previously it would only collapse when hitting ESC when focused on the form or when clicking outside of the form.
  • Add translation for aria-label.

File Block

Loading strategy: defer

The view script is just responsible for hiding PDF embeds for browsers that don't support them, so the defer loading strategy is applied since this query for File blocks is done at DOMContentLoaded. Since Gutenberg prints block scripts in the head, the defer strategy prevents it from blocking rendering. It is less likely that a File block would appear in the initial viewport compared with the Navigation and Search blocks, so this is another reason why it is deferred to run once the DOM loads.

Testing Instructions

I've configured the header in a block theme as follows, with two row variations:

image

In Twenty Twenty-Three with WordPress 6.3-beta4:

  1. Add a plugin that turns off the Interactivity API (see Add filter to turn off Interactivity API for a block #52579). This is disabled because I wanted to test the performance in core which does not use this experiment. You can use this plugin code: add_filter( 'gutenberg_should_block_use_interactivity_api', '__return_false' ).
  2. In the Site Editor, open the Header template part:
    a. Populate a Navigation with nested submenus, ensure the overlay menu option (responsive menu) is enabled, as well as the open on click submenus. This causes both view scripts to be enqueued. Make sure that one of the links in the Navigation is for an anchor link, such as to #respond, which is important to test clicks when the responsive menu is open. (In my test, I have two copies of the Navigation block, one in which the overlay menu is off and another in which it is always.)
    b. Configure a Search block so that it is "button only". This causes the view script to get enqueued. (I found this a bit tricky to do, as I had to re-select the same option a couple times.)
    c. Optionally duplicate a row containing the two previous blocks and add more variations.
  3. Create a post with a File block with a PDF embed including the "show inline embed" option.
  4. View the post on the frontend.

The head should include:

<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/search/view.min.js?ver=0328db8c33caa6bee0df' id='wp-block-search-view-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/navigation/view.min.js?ver=fc2b783ad32a391b367e' id='wp-block-navigation-view-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/navigation/view-modal.min.js?ver=457e4b6ea11766d7ed40' id='wp-block-navigation-view-2-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/file/view.min.js?ver=7a5cb419b4808ec7a475' id='wp-block-file-view-js' defer data-wp-strategy='defer'></script>

Note how the 4 view scripts for the Search block, Navigation block, and File block are all defer.

Note that this is the case I'm most interested in because it is what is currently reflected in core, since the Interactivity API is experimental.

Expected markup when Interactivity API is enabled

When the Interactivity API is enabled, the head should contain:

<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/search/view.min.js?ver=0328db8c33caa6bee0df' id='wp-block-search-view-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/interactivity/index.min.js?ver=1689643390' id='wp-interactivity-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/navigation/view-interactivity.min.js?ver=3dae10865f3f5dd45c1d' id='wp-block-navigation-view-js' defer data-wp-strategy='defer'></script>
<script src='http://localhost:8888/wp-content/plugins/gutenberg/build/block-library/blocks/file/view-interactivity.min.js?ver=033f781a5a681b19ffeb' id='wp-block-file-view-js' defer data-wp-strategy='defer'></script>

Note that the Search block has not yet been updated to use the Interactivity API.

Performance Analysis

Before and after the branch for this PR was checked out, I ran the following on the Gutenberg development environment (wp-env):

npm run research -- benchmark-web-vitals -u http://localhost:8888/?p=8 -n 20 -p

And I combined the two sets of results into a single table showing the difference for the FCP, LCP, and LCP-TTFB metrics:

Metric Before (ms) After (ms) Diff (ms) Diff (%)
FCP (p10) 96.17 94.58 -1.59 -1.7%
FCP (p25) 98.65 97.42 -1.23 -1.2%
FCP (p50) 112.35 104.85 -7.5 -6.7%
FCP (p75) 121.2 116.55 -4.65 -3.8%
FCP (p90) 130.01 125.59 -4.42 -3.4%
LCP (p10) 96.17 94.58 -1.59 -1.7%
LCP (p25) 98.65 97.42 -1.23 -1.2%
LCP (p50) 112.35 104.85 -7.5 -6.7%
LCP (p75) 121.2 117.45 -3.75 -3.1%
LCP (p90) 130.01 127.3 -2.71 -2.1%
LCP-TTFB (p10) 43.31 35.81 -7.5 -17.3%
LCP-TTFB (p25) 44.72 39.5 -5.22 -11.7%
LCP-TTFB (p50) 50.8 41 -9.8 -19.3%
LCP-TTFB (p75) 56.68 50.75 -5.93 -10.5%
LCP-TTFB (p90) 62.42 56.25 -6.17 -9.9%

Of particular note, the median of requests experienced a 19.3% reduction in LCP-TTFB. This metric discounts TTFB which would be the case when good page caching is in place.

I tested on an HP Dragonfly Chromebook running in the Linux environment. Testing on a more powerful Macbook Pro would surely yield less impressive results. Conversely, testing on a low-powered Android phone should be even more impressive.

@westonruter westonruter added [Type] Enhancement A suggestion for improvement. [Block] Search Affects the Search Block - used to display a search field [Block] Comments Affects the Comments Block - formerly known as Comments Query Loop [Block] Post Comments Form Affects the Comments Form Block [Feature] Interactivity API API to add frontend interactivity to blocks. labels Jul 11, 2023
@westonruter
Copy link
Member Author

For the Navigation block and the Search block, it would actually be ideal to load them in the head with the async strategy if they did not depend on the DOM being loaded entirely (i.e. waiting for DOMContentLoaded event). This is because the header may be shown long before the footer gets parsed, such as on a connection where the page is loading slowly. If the view script for the Search block and Navigation block could be refactored to use event delegation, then they could be loaded in the head with an async strategy and there would be less risk of a user rage clicking the Search button or Navigation button without anything happening.

@github-actions

This comment was marked as off-topic.

…dd/defer-script-loading-strategy

* 'trunk' of https://github.com/WordPress/gutenberg:
  Update Changelog for 16.2.0
  Adding support for defined IDs in `TextControl` component (#52028)
  Bump plugin version to 16.2.0
  Revert "Bump plugin version to 16.2.0"
  Bump plugin version to 16.2.0
  Add maxLength to LinkControl search items (#52523)
  [RNMobile] Update Editor block inserter button styles and default text input placeholder/selection styles (#52269)
  Site Editor: Reset device preview type when exiting the editing mode (#52566)
  Trim footnote anchors from excerpts (#52518)
  Add back old Navigation and File blocks JavaScript implementation when Gutenberg is not installed (#52553)
  Block Editor: Ensure synced patterns are accounted for in 'getAllowedBlocks' (#52546)
  Fix md5 class messed up with new block key (#52557)
  Fix entity cache misses for single posts due to string as recordKey (#52338)
  Make "My patterns" permanently visible (#52531)
  Hide site hub when resizing frame upwards to avoid overlap (#52180)
  Fix "Manage all patterns" link appearance (#52532)
  Update navigation menu title size & weight in detail panels (#52477)
  Site Editor Patterns: Ensure sidebar does not shrink when long pattern titles are used (#52547)
  Site Editor: Restore quick inserter 'Browse all' button (#52529)
  Patterns: update the title of Pattern block in the block inspector card  (#52010)
@westonruter westonruter marked this pull request as draft July 12, 2023 17:52
@westonruter
Copy link
Member Author

Switching back to draft, because as of #52553 the old non-Interactivity API implementations of the File block and Navigation block are available again. I'll ensure they are deferred (or made async) as well.

@westonruter westonruter added the [Block] Navigation Affects the Navigation Block label Jul 12, 2023
@github-actions
Copy link

github-actions bot commented Jul 13, 2023

Size Change: +196 B (0%)

Total Size: 1.44 MB

Filename Size Change
build/block-library/blocks/navigation/view-modal.min.js 2.85 kB +65 B (+2%)
build/block-library/blocks/navigation/view.min.js 469 B +31 B (+7%) 🔍
build/block-library/blocks/search/view.min.js 631 B +100 B (+19%) ⚠️
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.69 kB
build/api-fetch/index.min.js 2.28 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 451 B
build/block-directory/index.min.js 6.99 kB
build/block-directory/style-rtl.css 1.02 kB
build/block-directory/style.css 1.02 kB
build/block-editor/content-rtl.css 4.26 kB
build/block-editor/content.css 4.25 kB
build/block-editor/default-editor-styles-rtl.css 381 B
build/block-editor/default-editor-styles.css 381 B
build/block-editor/index.min.js 210 kB
build/block-editor/style-rtl.css 14.8 kB
build/block-editor/style.css 14.8 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 126 B
build/block-library/blocks/audio/theme.css 126 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 584 B
build/block-library/blocks/button/editor.css 582 B
build/block-library/blocks/button/style-rtl.css 624 B
build/block-library/blocks/button/style.css 623 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 409 B
build/block-library/blocks/columns/style.css 409 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.61 kB
build/block-library/blocks/cover/style.css 1.6 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 178 B
build/block-library/blocks/details/style.css 178 B
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 126 B
build/block-library/blocks/embed/theme.css 126 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/style-rtl.css 269 B
build/block-library/blocks/file/style.css 270 B
build/block-library/blocks/file/view-interactivity.min.js 317 B
build/block-library/blocks/file/view.min.js 375 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/freeform/editor-rtl.css 2.58 kB
build/block-library/blocks/freeform/editor.css 2.58 kB
build/block-library/blocks/gallery/editor-rtl.css 947 B
build/block-library/blocks/gallery/editor.css 952 B
build/block-library/blocks/gallery/style-rtl.css 1.53 kB
build/block-library/blocks/gallery/style.css 1.53 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 336 B
build/block-library/blocks/html/editor.css 337 B
build/block-library/blocks/image/editor-rtl.css 834 B
build/block-library/blocks/image/editor.css 833 B
build/block-library/blocks/image/style-rtl.css 1.42 kB
build/block-library/blocks/image/style.css 1.42 kB
build/block-library/blocks/image/theme-rtl.css 126 B
build/block-library/blocks/image/theme.css 126 B
build/block-library/blocks/image/view-interactivity.min.js 1.46 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 507 B
build/block-library/blocks/media-text/style.css 505 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 712 B
build/block-library/blocks/navigation-link/editor.css 711 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 296 B
build/block-library/blocks/navigation-submenu/editor.css 295 B
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.23 kB
build/block-library/blocks/navigation/style.css 2.22 kB
build/block-library/blocks/navigation/view-interactivity.min.js 988 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 401 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 174 B
build/block-library/blocks/paragraph/editor.css 174 B
build/block-library/blocks/paragraph/style-rtl.css 279 B
build/block-library/blocks/paragraph/style.css 281 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 588 B
build/block-library/blocks/post-featured-image/editor.css 586 B
build/block-library/blocks/post-featured-image/style-rtl.css 319 B
build/block-library/blocks/post-featured-image/style.css 319 B
build/block-library/blocks/post-navigation-link/style-rtl.css 153 B
build/block-library/blocks/post-navigation-link/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 314 B
build/block-library/blocks/post-template/style.css 314 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 302 B
build/block-library/blocks/query-pagination/style.css 299 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 450 B
build/block-library/blocks/query/editor.css 449 B
build/block-library/blocks/quote/style-rtl.css 222 B
build/block-library/blocks/quote/style.css 222 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 178 B
build/block-library/blocks/search/editor.css 178 B
build/block-library/blocks/search/style-rtl.css 587 B
build/block-library/blocks/search/style.css 584 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 323 B
build/block-library/blocks/shortcode/editor.css 323 B
build/block-library/blocks/site-logo/editor-rtl.css 754 B
build/block-library/blocks/site-logo/editor.css 754 B
build/block-library/blocks/site-logo/style-rtl.css 203 B
build/block-library/blocks/site-logo/style.css 203 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.43 kB
build/block-library/blocks/social-links/style.css 1.42 kB
build/block-library/blocks/spacer/editor-rtl.css 348 B
build/block-library/blocks/spacer/editor.css 348 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 433 B
build/block-library/blocks/table/editor.css 433 B
build/block-library/blocks/table/style-rtl.css 645 B
build/block-library/blocks/table/style.css 644 B
build/block-library/blocks/table/theme-rtl.css 146 B
build/block-library/blocks/table/theme.css 146 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 174 B
build/block-library/blocks/video/style.css 174 B
build/block-library/blocks/video/theme-rtl.css 126 B
build/block-library/blocks/video/theme.css 126 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.1 kB
build/block-library/common.css 1.1 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.1 kB
build/block-library/editor.css 12.1 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 202 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 13.7 kB
build/block-library/style.css 13.7 kB
build/block-library/theme-rtl.css 686 B
build/block-library/theme.css 691 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 51 kB
build/commands/index.min.js 14.9 kB
build/commands/style-rtl.css 827 B
build/commands/style.css 827 B
build/components/index.min.js 241 kB
build/components/style-rtl.css 11.8 kB
build/components/style.css 11.8 kB
build/compose/index.min.js 12 kB
build/core-commands/index.min.js 2.31 kB
build/core-data/index.min.js 16.4 kB
build/customize-widgets/index.min.js 12 kB
build/customize-widgets/style-rtl.css 1.46 kB
build/customize-widgets/style.css 1.45 kB
build/data-controls/index.min.js 640 B
build/data/index.min.js 8.28 kB
build/date/index.min.js 17.8 kB
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.63 kB
build/edit-post/classic-rtl.css 544 B
build/edit-post/classic.css 545 B
build/edit-post/index.min.js 35.3 kB
build/edit-post/style-rtl.css 7.58 kB
build/edit-post/style.css 7.57 kB
build/edit-site/index.min.js 89.2 kB
build/edit-site/style-rtl.css 13.1 kB
build/edit-site/style.css 13.1 kB
build/edit-widgets/index.min.js 16.9 kB
build/edit-widgets/style-rtl.css 4.52 kB
build/edit-widgets/style.css 4.52 kB
build/editor/index.min.js 45.4 kB
build/editor/style-rtl.css 3.54 kB
build/editor/style.css 3.53 kB
build/element/index.min.js 4.8 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 7.62 kB
build/format-library/style-rtl.css 554 B
build/format-library/style.css 553 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 448 B
build/i18n/index.min.js 3.58 kB
build/interactivity/index.min.js 10.4 kB
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.64 kB
build/keycodes/index.min.js 1.84 kB
build/list-reusable-blocks/index.min.js 2.18 kB
build/list-reusable-blocks/style-rtl.css 836 B
build/list-reusable-blocks/style.css 836 B
build/media-utils/index.min.js 2.9 kB
build/notices/index.min.js 948 B
build/nux/index.min.js 1.99 kB
build/nux/style-rtl.css 735 B
build/nux/style.css 732 B
build/plugins/index.min.js 1.77 kB
build/preferences-persistence/index.min.js 1.84 kB
build/preferences/index.min.js 1.24 kB
build/primitives/index.min.js 943 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 951 B
build/react-i18n/index.min.js 615 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.7 kB
build/reusable-blocks/index.min.js 2.71 kB
build/reusable-blocks/style-rtl.css 243 B
build/reusable-blocks/style.css 243 B
build/rich-text/index.min.js 11 kB
build/router/index.min.js 1.77 kB
build/server-side-render/index.min.js 1.94 kB
build/shortcode/index.min.js 1.39 kB
build/style-engine/index.min.js 1.83 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.57 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 958 B
build/warning/index.min.js 268 B
build/widgets/index.min.js 7.16 kB
build/widgets/style-rtl.css 1.15 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.02 kB

compressed-size-action

…dd/defer-script-loading-strategy

* 'trunk' of https://github.com/WordPress/gutenberg: (24 commits)
  Add filter to turn off Interactivity API for a block (#52579)
  Search: Remove unnecessary useEffect (#52604)
  Navigation: Simplify the useSelect for useNavigationMenus (#51977)
  Item: Unify focus style and add default font styles (#52495)
  Update Changelog for 16.2.1
  Bump plugin version to 16.2.1
  Avoid passing undefined `selectedBlockClientId` in `BlockActionsMenu` (#52595)
  Cover Block: Fix block deprecation when fixed background is enabled (#51612)
  Nav block: link text color inheritance fixes and tests (#51710)
  Stabilize defaultBlock, directInsert API's and getDirectInsertBlock selector (#52083)
  Fix console warning by improving error handling in Nav block classic menu conversion (#52591)
  Fix: Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. (#52415)
  Lodash: Remove remaining `_.get()` from block editor and deprecate (#52561)
  Fix importing classic menus (#52573)
  ResizableFrame: Make keyboard accessible (#52443)
  Site Editor: Fix navigation menu sidebar actions order and label (#52592)
  correct a typo: sapce -> space (#52578)
  Avoid errors in Dimension visualizers when switching between iframed and non-iframed editors (#52588)
  Patterns: Add client side pagination to patterns list (#52538)
  Site Editor: Make sidebar back button go *back* instead of *up* if possible (#52456)
  ...
* Fully leverage event delegation to allow async loading strategy.
* Keep track of whether a submenu is open to short-circuit event handlers.
* Use passive event listeners.
* Fully leverage event delegation to allow async loading strategy.
* Remove event listener for anchor clicks in modal when modal is closed.
* Use passive event listeners.
* Adopt asynchronous loading strategy.
* Use event delegation.
* Collapse expanded blocks when tabbing out of expanded Search block.
* Only attach keydown/keyup event handlers while Search block is expanded.
* Ensure search button's aria-label is translated.
* Ensure Search button's type is restored to 'button' instead of deleting type (since no type is same as 'submit').
* Use passive event listeners.
@westonruter
Copy link
Member Author

In that case I agree we should only use defer on <head> scripts.

I've commented on Core-54018 to inquire if there are any cases where a block's view script has to be executed in the head. If not, then it seems we should defer execution while keeping in the head rather than move to the footer. Then there would be no need to introduce a capability in block.json to dictate whether a block script is executed in the head or footer (#46954), and it seems there isn't a need to allow them to be async either.

@westonruter
Copy link
Member Author

Yes, I agree. I was wary to take the step of making all block scripts defer by default for this initial PR as that may negatively impact blocks in plugins. I'm not sure why block scripts are in the head by default. Maybe @gziolo knows? It seems they may have been in the footer initially and accidentally got moved to the head (which, if so, caused a regression which I fixed in #50113).

Actually, I think I know what happened. When using a classic theme, the view scripts for blocks actually get printed in the footer. This is because blocks are parsed in the middle of template rendering, which means that view scripts are enqueued too later for printing at wp_head even though they have in_footer set to false. For block themes, however, parsing is done before template rendering, which means any enqueued block view script will get printed at wp_head.

Given that classic themes all print in the footer, it seems there is absolutely no reason to not add defer. This won't really benefit classic themes at all, but it will have a big impact for block themes.

Copy link
Member

@luisherranz luisherranz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree head and defer is always the best option for block scripts. Thanks for taking care of this Weston :)

Could you share your merging plan?

  • Will you wait for WP 6.3 release and then set WP 6.3 as the lowest required version for Gutenberg?
  • Are you planning to integrate the new strategy implementation into Gutenberg's compatibility mechanism? (Is it already present?)
  • Is the plan that the strategy applies when users are on WP 6.3, but doesn't apply to those using older WP versions?

Please notify me if you're opting for the final approach because merging this as it is will break the Interactivity API scripts.

Comment on lines 16 to 24
wp_script_add_data( 'wp-interactivity', 'strategy', 'defer' );

// Move all the view scripts of the interactive blocks to the footer.
$registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
foreach ( array_values( $registered_blocks ) as $block ) {
if ( isset( $block->supports['interactivity'] ) && $block->supports['interactivity'] ) {
foreach ( $block->view_script_handles as $handle ) {
wp_script_add_data( $handle, 'group', 1 );
wp_script_add_data( $handle, 'strategy', 'defer' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that if we are making all frontend block scripts deferred by default, we don't need to do it for the supports.interactivity ones again. We still need to add defer to wp-interactivity, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Done in aab0b4b

@sgomes
Copy link
Contributor

sgomes commented Jul 20, 2023

Thank you for taking care of this change so quickly, @westonruter! 🙌 defer in <head> is definitely the best approach for these scripts, in my opinion 👍

@westonruter
Copy link
Member Author

Could you share your merging plan?

  • Will you wait for WP 6.3 release and then set WP 6.3 as the lowest required version for Gutenberg?
    ...
  • Is the plan that the strategy applies when users are on WP 6.3, but doesn't apply to those using older WP versions?

Please notify me if you're opting for the final approach because merging this as it is will break the Interactivity API scripts.

@luisherranz I've added a version check to continue loading in the footer if on WP<6.3. In 6.3+, the scripts will load in the head but defer execution.

  • Are you planning to integrate the new strategy implementation into Gutenberg's compatibility mechanism? (Is it already present?)

I'm not sure I understand.

@luisherranz
Copy link
Member

@luisherranz I've added a version check to continue loading in the footer if on WP<6.3. In 6.3+, the scripts will load in the head but defer execution.

Perfect, thank you! 🙂

Are you planning to integrate the new strategy implementation into Gutenberg's compatibility mechanism? (Is it already present?)

I'm not sure I understand.

Oh, sorry. I'm not versed in this aspect of Gutenberg, but I was wondering if you had any intention of adding it to the compat folder (https://github.com/WordPress/gutenberg/tree/trunk/lib/compat), which, if I understand correctly, includes necessary code that may be missing if WordPress is on an earlier version. But I guess you don't have plans for that, right?

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great to me 🎉

My only pushback would be on the comment-reply script changes, as they feel unrelated, and potentially even for core not beneficial. I think we should decouple that conversation and remove the change from here.

packages/block-library/src/comments/index.php Outdated Show resolved Hide resolved
packages/block-library/src/post-comments-form/index.php Outdated Show resolved Hide resolved
@westonruter
Copy link
Member Author

Oh, sorry. I'm not versed in this aspect of Gutenberg, but I was wondering if you had any intention of adding it to the compat folder (https://github.com/WordPress/gutenberg/tree/trunk/lib/compat), which, if I understand correctly, includes necessary code that may be missing if WordPress is on an earlier version. But I guess you don't have plans for that, right?

@luisherranz Yeah, I'm not so well versed in it either 😊

I don't think it's feasible to extract the implementation of the script loading strategies into compat. It's really something hooked deep into the bowels of WP_Scripts. And with a version check to continue printing in the footer in WP<6.3, I don't think it's necessary. That being said, we could provide a compat implementation of defer scripts for just our own block view scripts which we know do not have any dependencies or dependents. This would involve the classic approach filtering script_loader_tag.

@westonruter
Copy link
Member Author

westonruter commented Jul 20, 2023

My only pushback would be on the comment-reply script changes, as they feel unrelated, and potentially even for core not beneficial. I think we should decouple that conversation and remove the change from here.

@felixarntz Sounds good to me. However, in core there may still be a benefit in that as I understand defer is also a signal for priority. If something is in the footer and it has defer then I believe a non-defer footer script would be prioritized. Then again, I wonder if actually comment-reply would actually make more sense as async since whether defer or blocking, both will delay DOMContentLoaded. Since comment-reply is only relevant when the user has scrolled down to comments, we really should delay it as late as possible, even lazily-load it when the user scrolls down (which is not yet a capability we have in core as you know).

@felixarntz
Copy link
Member

Thanks @westonruter, I suggest we open a Trac ticket to discuss this further.

@westonruter westonruter changed the title Use async and defer loading strategies for frontend view scripts Use defer loading strategy for frontend view scripts Jul 20, 2023
@westonruter
Copy link
Member Author

westonruter commented Jul 20, 2023

I suggest we open a Trac ticket to discuss this further.

Filed: Delay loading comment-reply script with async loading strategy (Core-58870)

cc @sgomes as I suggest the async strategy for the reasons in the ticket.

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, LGTM code-wise!

There's a failure in https://github.com/WordPress/gutenberg/actions/runs/5614408652/job/15212506354?pr=52536, but seems unrelated?

…ding-strategy

* origin/trunk: (59 commits)
  Promisify action creator return type for WP data dispatch (#52530)
  [RNMobile] Add WP hook for registering non-core blocks (#52791)
  removes check for active preview device type to enable the fixed toolbar preference (#52770)
  Enforce checks against redeclaration for functions and classes (#52696)
  update appearance tools, (#52785)
  Behaviors: Extend Global Styles API to read/write behaviors config. (#52370)
  HeaderToolbar - Update inserterMethod meta data (#52735)
  add options for debugging php unit tests (#52778)
  Docs: Interactivity API > Getting Started Guide - minor adjustments (#52786)
  Footnotes: Use static closures when not using '' (#52781)
  Improve slug generation & matching in request utils (#52414)
  Open "docs" folder for the Interactivity API package and Getting Started Guide (#52462)
  Global Styles: Don't use named arguments for 'sprintf' (#52782)
  E2E utils - Update locator to hide the keyboard on iOS to pick the first element, on iPad two buttons are available and the second one makes the floating keyboard to show up (#52771)
  Patterns: Reinstate template parts mode spec (#52780)
  chore(release): publish
  Update changelog files
  Patterns: Fix empty general template parts category (#52747)
  Add id to pattern inserted notice to stop multiple notices stacking (#52746)
  Site Editor: Fix site link accessibility issues (#52744)
  ...
@westonruter
Copy link
Member Author

There's a failure in https://github.com/WordPress/gutenberg/actions/runs/5614408652/job/15212506354?pr=52536, but seems unrelated?

Seems so. E2E is also failing in trunk.

@luisherranz
Copy link
Member

with a version check to continue printing in the footer in WP<6.3, I don't think it's necessary

I agree 🙂

@sgomes
Copy link
Contributor

sgomes commented Jul 21, 2023

cc @sgomes as I suggest the async strategy for the reasons in the ticket.

Thank you, @westonruter! 👍 I left a more elaborate comment there, but TL;DR: both async in the footer and defer in the <head> seem like reasonable options.

lib/blocks.php Show resolved Hide resolved
@westonruter westonruter merged commit 6cf6643 into trunk Jul 25, 2023
@westonruter westonruter deleted the add/defer-script-loading-strategy branch July 25, 2023 22:10
@github-actions github-actions bot added this to the Gutenberg 16.4 milestone Jul 25, 2023
* $script_dependencies,
* - isset( $script_asset['version'] ) ? $script_asset['version'] : false
* + isset( $script_asset['version'] ) ? $script_asset['version'] : false,
* + array( 'strategy' => 'defer' )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@westonruter, do we have a patch against WordPress core that sets the default strategy for viewScripts to defer?

Copy link
Member

@gziolo gziolo Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm catching up after an extended leave, so I might find the answer myself in the meantime as I'm reading the communication related to the defer strategy. I'm mostly willing to ensure we have better defaults in WP core, so people have performant configuration by design.

At the moment, I don't see any changes applied in core:

https://github.com/WordPress/wordpress-develop/blob/f107073f39827c2ab2b3b3198e194f844f357213/src/wp-includes/blocks.php#L169-L174

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch hasn't been backported to core yet. I wasn't sure when that should be done, if it should sit in Gutenberg a bit first to test and then propose for core, or to add it to core at the same time. I'm happy to open a backport ticket and open a PR to apply this patch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Committed to core: r56398

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's enable it for all blocks in both the Gutenberg plugin, and in WordPress core to get as much testing as necessary with 3rd party blocks that use block.json. I see that you discussed it in the context of view scripts in #52536 (comment) and ruled out as a potential issue.

Committed to core: r56398

Noting that with this patch, defer is going to be used with editorScript, script, and viewScript. In the case of editorScript, the dependencies aren't using defer like wp-block or wp-block-editor so it will probably fallback to the legacy handling. I'll comment on Trac, too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's enable it for all blocks in both the Gutenberg plugin, and in WordPress core to get as much testing as necessary with 3rd party blocks that use block.json. I see that you discussed it in the context of view scripts in #52536 (comment) and ruled out as a potential issue.

Yes, it should be enabled for all blocks, whether core or 3rd party.

Noting that with this patch, defer is going to be used with editorScript, script, and viewScript. In the case of editorScript, the dependencies aren't using defer like wp-block or wp-block-editor so it will probably fallback to the legacy handling. I'll comment on Trac, too.

Oh, good catch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Comments Affects the Comments Block - formerly known as Comments Query Loop [Block] Navigation Affects the Navigation Block [Block] Post Comments Form Affects the Comments Form Block [Block] Search Affects the Search Block - used to display a search field [Feature] Interactivity API API to add frontend interactivity to blocks. [Type] Enhancement A suggestion for improvement.
Projects
None yet
8 participants