WordPress.org

Make WordPress Core

Opened 16 months ago

Closed 4 months ago

Last modified 4 months ago

#50328 closed enhancement (fixed)

Enqueue script and style assets only for blocks present on the page

Reported by: aduth Owned by: gziolo
Milestone: 5.8 Priority: normal
Severity: normal Version:
Component: Editor Keywords: has-patch has-unit-tests commit needs-dev-note
Focuses: javascript Cc:

Description

Related: https://github.com/WordPress/gutenberg/pull/22754
Related: #49926

Context:

Currently, all scripts and styles for all blocks will be rendered in every front-end page. This is hugely wasteful, since in reality only the scripts and styles associated with blocks relevant for that page should be enqueued.

In part, this is made difficult by the fact that it's quite common to enqueue block assets as part of the enqueue_block_assets action, which is called not specifically for any one block type, but rather once per page load.

Proposal:

Only block assets (scripts and styles) of blocks present in the page should be enqueued.

Implementation Specifics:

See also relevant Gutenberg proposal changes: https://github.com/WordPress/gutenberg/pull/22754

While the previously-mentioned behavior of enqueue_block_assets may not be reconcilable with the desire to load assets relevant for the page, it should be possible for a block implementer to opt in to this behavior using the (already supported) script and style arguments of register_block_type.

Backward Compatibility:

See also discussion from: https://github.com/WordPress/gutenberg/pull/22754

Notably this comment:

I think it's something that, if implemented, should warrant some release devnotes explaining the change.
The broad answer for me is that it does change some behaviors, but not anything that was ever particularly documented as expected, or practically speaking would be common to depend upon.
Specifically, these changes could include:
If one was to rely on some block script to define some functionality leveraged outside the script and on pages where the block isn't present, those scripts would now only be enqueued on pages where the block is present. This is very similar case to "incidental availability" of script functionality from dependencies of other scripts, and is more an error on the part of the developer where any such script should be defined as an explicit dependency of the script.
If one was to rely on some block stylesheet to style pages where that block is not present, it would now only be enqueued on pages where the block is present. It is not clear there would be any reason someone would have done this, since the purpose of a block stylesheet should be to style the block (or even if it was styling the page, at least only the page where the block is present).
If one was to rely on the particular page enqueue lifecycle (header, footer placement), those expectations could change. This is documented in more detail in the original comment, and is potentially not an unsolvable challenge to preserve the current behavior if problematic.
It could also use some further testing regarding the comment at #21838 (comment) of more specialized use-cases, though the later comment #21838 (comment) seems to imply that the general approach is compatible.

Another potential use-case described at https://github.com/WordPress/gutenberg/pull/22754#issuecomment-639079262, though granted to be an uncommon scenario.

Attachments (3)

50328-test.diff (6.2 KB) - added by gziolo 15 months ago.
Test version - work in progress
50328-skip-assets.diff (6.3 KB) - added by gziolo 15 months ago.
Test with the logic to skip assets when not on the front
50328.3.patch (3.1 KB) - added by hellofromTonya 5 months ago.
Renames filter and function for consistency

Download all attachments as: .zip

Change History (41)

#1 @westonruter
16 months ago

Cross-posting Gutenberg issue comments where I've raised a concern about FOUC:

Has it been considered to not just enqueue the scripts and styles but rather print them instead? This will reduce the risk of a flash of unstyled content, as otherwise enqueued assets will get printed at wp_footer, correct?

I took this approach in the Syntax-highlighting Code Block plugin. It's definitely not as clean as just deferring to enqueue in gutenberg#22754, but it did address FOUC.

See also atomic-blocks#294 & atomic-blocks#297 which do this for Atomic Blocks, to conditionally print the Font Awesome stylesheet before the first dependent block in the page.

#2 @gziolo
15 months ago

  • Milestone changed from Awaiting Review to 5.5
  • Owner set to gziolo
  • Status changed from new to assigned

@gziolo
15 months ago

Test version - work in progress

#3 follow-up: @gziolo
15 months ago

@westonruter, I'm trying to print those styles and scripts on the front-end using the approach found in the plugin you shared:

https://github.com/westonruter/syntax-highlighting-code-block/blob/e652901420ee6991213eba11e7c8c52176e84e0c/syntax-highlighting-code-block.php#L346-L352

It works for the front-end perfectly fine, but I run into an issue in the editor. If the block isn't inserted into a post, everything works correctly. As soon as insert it into the post, it renders JS and CSS inside the preloaded REST API call for the post object :)

So I have two questions:

  1. Is the approach I took valid, or did I misunderstood what you were proposing?
  2. If (1) is on track, any thoughts on how I can prevent rendering CSS/JS inside the preloaded REST API call :)

@gziolo
15 months ago

Test with the logic to skip assets when not on the front

#4 follow-up: @gziolo
15 months ago

It looks like the following check fixes the issue for the editor:

<?php
$skip_assets = is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST );

I'm not sure it's the best way to approach it though.

#5 in reply to: ↑ 4 @westonruter
15 months ago

Replying to gziolo:

It looks like the following check fixes the issue for the editor:

<?php
$skip_assets = is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST );

I'm not sure it's the best way to approach it though.

This is similar to what was done for Atomic Blocks in https://github.com/studiopress/atomic-blocks/pull/297

So I think that approach makes sense. I didn't need to do this in the syntax-highlighting-code-block plugin because the CSS is only ever added in the render_callback on the frontend. So the CSS is not needed in the editor.

#6 in reply to: ↑ 3 @westonruter
15 months ago

Replying to gziolo:

  1. Is the approach I took valid, or did I misunderstood what you were proposing?

And yes, the approach you took is the same I took in Atomic Blocks, except I had to use the render_block filter naturally.

I do have one potential issue to simply prepending the link, style, and script tags. Some themes (actually, quite a few) have CSS that target top-level elements of the content via CSS like .entry-content > *.

Consider a theme that does this:

.entry-content > * {
    display: block;
}

This would have negative results! (I faced this on an actual site.)

For this reason, in my plugin I actually inject the style markup inside the block's opening start tag.

Nevertheless, this won't work in the general case, as it can break CSS that is trying to target a block's :first-child.

Maybe themes are just _doing_it_wrong by targeting .entry-content > * with display properties and this should be fixed with education. But I see another potential problem: consider CSS that is trying to target a given block that appears right after another one: p + .wp-block-illustration. If there is CSS that gets extracted from such an Illustration block to be printed before the block, then this will break the selector.

One area to explore could be to extract the styles and scripts during do_blocks and then prepend them all to the $output of that function. That would improve the sibling selector situation, but it would not help with the .entry-content > *:first-child case.

I don't have a conclusion here, nor am I entirely sure how much of a problem this will with sites generally. But I wanted to raise this as an area of concern.

#7 follow-up: @gziolo
15 months ago

@westonruter, it all edge cases makes me wonder whether the initial approach proposed by @aduth wouldn't be more reliable in the majority of cases. Plugin developers can always opt-out from using style or script when registering a block and handle everything themselves.

Shouldn't we seek for an alternative proposal that would make it easier to print CSS or JS inside the rendered block?

There is also this question @matveb asked about sites with infinite scrolls and how it would work with the current proposal:
https://github.com/WordPress/gutenberg/issues/21838#issuecomment-620586710

#8 in reply to: ↑ 7 @westonruter
15 months ago

Replying to gziolo:

it all edge cases makes me wonder whether the initial approach proposed by aduth wouldn't be more reliable in the majority of cases.

Are you referring to enqueueing the scripts/styles when the blocks are rendered and then printing the scripts/styles in the footer? The risk there is with FOUC. But FOUC is probably better than adding a lot of assets which are never used on the page. Nevertheless, FOUC can negatively impact Core Web Vitals, in particular largest contentful paint (LCP) and cumulative layout shift (CLS).

#9 @mcsf
15 months ago

Catching up on this ticket and taking a new look at Gutenberg PR 22754, it seems to me that we are risking a lot more bugs and breakage by switching from plain enqueueing to inline printing.

The original approach has been more thoroughly tested and, while flashes of unstyled content are something to avoid, they seem to be a better problem to have, one that can be mitigated elsewhere, and one especially mitigated by caching.

Maybe themes are just _doing_it_wrong by targeting .entry-content > * with display properties and this should be fixed with education.

I think this is the wrong way to look at it. Even when there are clear directives (e.g. deprecation of a set of private HTML classes) there are always issues of breakage because those directives were ignored. I can imagine a much messier situation if we retroactively burden themes with staying away from particular ways of styling .entry-content. It would be destructive for both existing sites and the block editor.


This question reminds me of how JS projects under webpack can be "tree-shaken" when modules are clear about whether they are "pure" or have side effects. Down the line (or sooner if the Beta period reveals this to be a more pressing issue), I can imagine a similar affordance in the block-rendering stack where admins can choose whether they prefer their dependencies inlined.

#10 @gziolo
15 months ago

  • Milestone changed from 5.5 to 5.6

We are reverting this functionality on the Gutenberg plugin side with https://github.com/WordPress/gutenberg/pull/23708. It's based on reports that it doesn't work for some folks in some cases. It's hard to tell what the condition is so we better move it to WordPress 5.6 to have more time to properly investigate.

This ticket was mentioned in Slack in #core-editor by gziolo. View the logs.


15 months ago

This ticket was mentioned in Slack in #core-restapi by sageshilling. View the logs.


15 months ago

This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.


12 months ago

#14 @johnbillion
12 months ago

  • Keywords early added
  • Milestone changed from 5.6 to Future Release

As there hasn't been any movement on this issue or the corresponding Gutenberg PR, let's punt it. It's something that needs to go in early in a cycle due to potential side effects.

This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.


10 months ago

#16 @hellofromTonya
10 months ago

@gziolo is this ticket on your 5.7 list?

#17 @gziolo
10 months ago

Yes, it is still in progress. A newly revised approach is going to be tested in the upcoming Gutenberg plugin release. PR from @aristath:
https://github.com/WordPress/gutenberg/pull/25220

This ticket was mentioned in PR #820 on WordPress/wordpress-develop by gziolo.


9 months ago

  • Keywords has-patch has-unit-tests added

Trac ticket: https://core.trac.wordpress.org/ticket/50328

Related Gutenberg PR: https://github.com/WordPress/gutenberg/pull/25220.

This change tries to align with the latest changes added by @aristath to the Gutenberg project. As part of styles splitting for core blocks, there was a special pattern introduced for how style handles are named. Ideally, we would apply it to all blocks but there might be some backward compatibility considerations so I left the handling for non-core blocks unchanged.

#19 @gziolo
9 months ago

In 49850:

Blocks: Align with Gutenberg the name of generated asset handle for core blocks

Related Gutenberg PR: https://github.com/WordPress/gutenberg/pull/25220.

It aligns with the latest changes added by aristath to the Gutenberg project. As part of styles splitting for core blocks, there was a special pattern introduced for how style handles are named. Ideally, we would apply it to all blocks but there might be some backward compatibility considerations so I left the handling for non-core blocks unchanged.

Props aristath.
See #50328.

This ticket was mentioned in Slack in #core-js by gziolo. View the logs.


6 months ago

This ticket was mentioned in PR #1236 on WordPress/wordpress-develop by aristath.


5 months ago

This PR backports all changes related to block styles loading from Gutenberg to core.

Trac ticket: https://core.trac.wordpress.org/ticket/50328

#22 @prbot
5 months ago

aristath commented on PR #1236:

This PR does the following:

#23 @youknowriad
5 months ago

  • Milestone changed from Future Release to 5.8

#24 @prbot
5 months ago

aristath commented on PR #1236:

Added the copied CSS files to the .gitignore list.

#25 @prbot
5 months ago

gziolo commented on PR #1236:

I performed a few tests with a demo post using all combinations of two filters that @aristath recommended:

{{{php
add_filter( 'separate_core_block_assets', 'return_true' );
add_filter( 'styles_inline_size_limit', '
return_zero' );
}}}

I wanted to add that most of the functionality was extensively tested in Gutenberg plugin by Ari and me a few weeks/months ago. The functionality also got some testing in the plugin and was iterated upon.

So far everything works as expected in my testing. I also plan to verify with a custom block that defines CSS in block.json files that gets registered on the server.

#26 @gziolo
5 months ago

  • Keywords commit needs-dev-note added; early removed

#27 @gziolo
5 months ago

A note to the better version of myself for future use. To ignore CSS files in src/wp-includes/blocks we need to set svn:ignore property for every folder. In case we add new folders, we will need to run the following again from the blocks folder or a modified version for a new folder:

svn propset svn:ignore '*.css' . --recursive

#28 @gziolo
5 months ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 50836:

Editor: Enqueue script and style assets only for blocks present on the page

Adds styles for individual core blocks to make it possible to render only styles for those blocks that are rendered on the page (frontend). This is optinal functionality for start that can be controlled with the new separate_core_block_assets filter.

In addition to that, styles can be inlined when path is passed when registering an individual styles. This functionality can be changed with the new styles_inline_size_limit filter. The maximum size of inlined styles in bytes defaults to 20 000.

Props aristath, aduth, westonruter, mcsf.
Fixes #50328, #52620.

#29 @prbot
5 months ago

gziolo commented on PR #1236:

Done with https://core.trac.wordpress.org/changeset/50836 🎉

Amazing work @aristath 👏🏻 👏🏻 👏🏻 👏🏻 👏🏻

This ticket was mentioned in Slack in #core-editor by gziolo. View the logs.


5 months ago

#31 @SergeyBiryukov
5 months ago

In 50838:

Editor: Some documentation and test improvements for loading separate assets for core blocks:

  • Move should_load_separate_core_block_assets() to a more appropriate place.
  • Update DocBlocks and inline comments per the documentation standards.
  • Document the $wp_styles global in wp_maybe_inline_styles().
  • List the expected result first in unit test assertions.
  • Remove a duplicate unit test.
  • Add missing @covers tags.

Follow-up to [50836], [50837].

See #50328, #52620, #53180.

#32 follow-up: @SergeyBiryukov
5 months ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

When reviewing [50836], I've noticed that we have a similarly named function and filter in script-loader.php:

  • The wp_should_load_block_editor_scripts_and_styles() function.
  • The should_load_block_editor_scripts_and_styles filter (same, but without the wp_ prefix).

Would it make sense to rename should_load_separate_core_block_assets() to match that for consistency? That would be:

  • The wp_should_load_separate_core_block_assets() function.
  • The should_load_separate_core_block_assets filter.

See also [49080] & [49093] / #51330 for more context.

@hellofromTonya
5 months ago

Renames filter and function for consistency

#33 in reply to: ↑ 32 @hellofromTonya
5 months ago

Replying to SergeyBiryukov:

Would it make sense to rename should_load_separate_core_block_assets() to match that for consistency? That would be:

  • The wp_should_load_separate_core_block_assets() function.
  • The should_load_separate_core_block_assets filter.

Yes, I agree with you, Sergey. Renaming here is consistent with existing patterns and aids in improving readability.

Patch 50328.3.patch applies your suggests.

#34 @gziolo
5 months ago

Sure, we can align. I assumed that should_load_separate_core_block_assets for the name of the filter is too long. I think there was a version closer to that in the initial proposal from @aristath.

#35 @SergeyBiryukov
5 months ago

Thanks! I also noticed that the filter name and description suggests that it controls the behavior of both styles and scripts, however it only controls styles at the moment.

Is the plan for it also to control scripts at some point? Otherwise, should it be updated to mention "styles" instead of "assets"?

#36 @gziolo
5 months ago

Is the plan for it also to control scripts at some point? Otherwise, should it be updated to mention "styles" instead of "assets"?

Yes, we definitely want to follow up with changes to how scripts are integrated. This is why we have a more general name. In practice, we have the first block that we hope to have integrated in time for WP 5.8 – File block.

#37 @gziolo
4 months ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 50919:

Editor: Rename should_load_separate_core_block_assets for consistency

Changes introduced:

  • The wp_should_load_separate_core_block_assets function.
  • The should_load_separate_core_block_assets filter.

Props hellofromTonya, SergeyBiryukov.
Fixes #50328.

#38 @johnbillion
4 months ago

In 51065:

Editor: Correct some docblocks added in [50836].

See #50328, #52620.

Note: See TracTickets for help on using tickets.