#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 has-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)
Change History (42)
#2
@
4 years ago
- Milestone changed from Awaiting Review to 5.5
- Owner set to gziolo
- Status changed from new to assigned
#3
follow-up:
↓ 6
@
4 years ago
@westonruter, I'm trying to print those styles and scripts on the front-end using the approach found in the plugin you shared:
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:
- Is the approach I took valid, or did I misunderstood what you were proposing?
- If (1) is on track, any thoughts on how I can prevent rendering CSS/JS inside the preloaded REST API call :)
#4
follow-up:
↓ 5
@
4 years 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
@
4 years 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
@
4 years ago
Replying to gziolo:
- 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:
↓ 8
@
4 years 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
@
4 years 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
@
4 years 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
@
4 years 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.
4 years ago
This ticket was mentioned in Slack in #core-restapi by sageshilling. View the logs.
4 years ago
This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.
4 years ago
#14
@
4 years 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.
4 years ago
#17
@
4 years 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.
4 years ago
#18
- 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.
This ticket was mentioned in Slack in #core-js by gziolo. View the logs.
4 years ago
This ticket was mentioned in PR #1236 on WordPress/wordpress-develop by aristath.
4 years ago
#21
This PR backports all changes related to block styles loading from Gutenberg to core.
Trac ticket: https://core.trac.wordpress.org/ticket/50328
4 years ago
#22
This PR does the following:
- Adds an action in Webpack to copy the separate block-styles to
wp-includes/blocks/{$block_name}/*.css
- Adds a
should_load_separate_block_assets
function andload_separate_block_assets
filter (see https://github.com/WordPress/gutenberg/blob/b786a9416b311fc550b22a50927b930a36568dca/lib/compat.php#L16-L26). The filter defaults tofalse
and is opt-in. - Adds a
wp_maybe_inline_styles
function (see https://github.com/WordPress/gutenberg/blob/4146d19b3bd276f4d6afbc7abc373b8ada18b63a/lib/blocks.php#L201-L269) - Tweaks the
register_block_style_handle
function to allow loading separate files instead of the monolithicstyle.css
file which contains styles for all blocks - Removes the
add_action( 'enqueue_block_assets', 'wp_enqueue_registered_block_scripts_and_styles' );
hook - Adds
path
data to block styles to allow inlining them - see https://core.trac.wordpress.org/ticket/52620. This was done in tandem with this PR because these 2 go hand-in-hand and doing them together is simpler
4 years ago
#25
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.
#27
@
4 years 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
4 years ago
#29
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.
4 years ago
#32
follow-up:
↓ 33
@
4 years 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 thewp_
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.
#33
in reply to:
↑ 32
@
4 years 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
@
4 years 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
@
4 years 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
@
4 years 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.
Cross-posting Gutenberg issue comments where I've raised a concern about FOUC: