Block Supports: Prevent Additional CSS duplication inside Query Loop#78282
Block Supports: Prevent Additional CSS duplication inside Query Loop#78282Mustafabharmal wants to merge 3 commits into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
t-hamano
left a comment
There was a problem hiding this comment.
Thanks for the PR, This PR might be considered for shipping in the 7.0 release as a performance fix.
My question is why special deduplication is only needed for custom CSS. The style engine should already have deduplication functionality. Perhaps there's a reason why custom CSS support couldn't be integrated into the style engine 🤔
@glendaviesnz will know better than I about the "why". I can say that the style engine already dedupes for rules inside a rendered stylesheet, but it's fed structured data (property/value pairs in declaration collections). My guess is that, because the custom CSS field accepts freeform text, it would have had to undergo a structural validation and sanitization step. There are a suite of scenarios where a standards-based CSS Processor could be the glue for all this. Just pulling a few examples:
I think @sirreal and friends might be looking at this sometime in the next couple of cycles. Don't quote me on it. I imagine, one day, the style engine could use this tool as a layer between ingestion and deduplication. |
|
This appears to be the right fix for now. It's contained and can be abstracted later. Some regression tests would be good, but I'll leave it you whether you do them here or in the Core backport patch 👍🏻 |
|
@Mustafabharmal, have you ever created a core backport patch? Could you try this out, referring to this document? |
| * Track which class names have already had their CSS enqueued to prevent | ||
| * duplicate styles when the same block is rendered multiple times inside | ||
| * a Query Loop (render_block_data fires once per loop iteration). | ||
| */ | ||
| wp_register_style( 'wp-block-custom-css', false, array( 'global-styles' ) ); | ||
| wp_add_inline_style( 'wp-block-custom-css', $processed_css ); | ||
| static $enqueued_class_names = array(); | ||
|
|
||
| if ( ! isset( $enqueued_class_names[ $class_name ] ) ) { | ||
| $enqueued_class_names[ $class_name ] = true; | ||
| /* | ||
| * Register and add inline style for block custom CSS. | ||
| * The style depends on global-styles to ensure custom CSS loads after | ||
| * and can override global styles. | ||
| */ | ||
| wp_register_style( 'wp-block-custom-css', false, array( 'global-styles' ) ); | ||
| wp_add_inline_style( 'wp-block-custom-css', $processed_css ); | ||
| } |
There was a problem hiding this comment.
Cross-posting my wordpress-develop comment: WordPress/wordpress-develop#11859 (comment)
A static variable can be very problematic for unit tests. I don't recommend using it. I suggest instead that we look at what wp_add_inline_style() is doing under the hood. It calls WP_Styles::add_inline_style( $handle, $code ) which in turn adds the provided code to an array of existing inline styles and then passes that amended array to WP_Styles::add_data( $handle, 'after', $after ).
So I think a better approach would be to use the underlying WP_Styles::$extra array instead of a static $enqueued_class_names here:
| * Track which class names have already had their CSS enqueued to prevent | |
| * duplicate styles when the same block is rendered multiple times inside | |
| * a Query Loop (render_block_data fires once per loop iteration). | |
| */ | |
| wp_register_style( 'wp-block-custom-css', false, array( 'global-styles' ) ); | |
| wp_add_inline_style( 'wp-block-custom-css', $processed_css ); | |
| static $enqueued_class_names = array(); | |
| if ( ! isset( $enqueued_class_names[ $class_name ] ) ) { | |
| $enqueued_class_names[ $class_name ] = true; | |
| /* | |
| * Register and add inline style for block custom CSS. | |
| * The style depends on global-styles to ensure custom CSS loads after | |
| * and can override global styles. | |
| */ | |
| wp_register_style( 'wp-block-custom-css', false, array( 'global-styles' ) ); | |
| wp_add_inline_style( 'wp-block-custom-css', $processed_css ); | |
| } | |
| * Track which class names have already had their CSS enqueued to prevent | |
| * duplicate styles when the same block is rendered multiple times inside | |
| * a Query Loop (render_block_data fires once per loop iteration). | |
| */ | |
| $handle = 'wp-block-custom-css'; | |
| if ( ! wp_style_is( $handle, 'registered' ) ) { | |
| wp_register_style( $handle, false, array( 'global-styles' ) ); | |
| } | |
| $after_styles = wp_styles()->get_data( $handle, 'after' ); | |
| if ( ! is_array( $after_styles ) ) { | |
| $after_styles = array(); | |
| } | |
| if ( ! in_array( $processed_css, $after_styles, true ) ) { | |
| wp_add_inline_style( $handle, $processed_css ); | |
| } |
|
@Mustafabharmal, do you have the bandwidth to address the feedback added to this PR and the core PR? Thank you! |
What?
Additional CSS added to a block inside a Query Loop was being inserted once per post in the loop, causing identical CSS rules to be duplicated N times in the output.
Closes #78278
Why?
gutenberg_render_custom_css_support_stylesis hooked torender_block_data, which fires on every block render. Inside a Query Loop the same block template is rendered once per post, sowp_add_inline_stylewas called N times with the same CSS content.Since
wp_unique_id_from_valuesdeterministically generates the same hash for the same block data, each loop iteration produced the same$class_nameand the same$processed_css.How?
Added a
static $enqueued_class_namesarray inside the function. Before callingwp_add_inline_style, we check whether CSS for this class name has already been enqueued. If it has, the call is skipped. The block's class name attribute is still set on every render (needed for correct markup), only the redundant style injection is prevented.Testing Instructions
<style id="wp-block-custom-css-inline-css">, the CSS rules should appear exactly once regardless of how many posts are in the loop.Screenshots or screencast
Before:
Test.Before.Fix.mov
After:
Test.After.Fix.mov