Make WordPress Core

Opened 18 months ago

Closed 6 months ago

#59462 closed enhancement (fixed)

Blocks: Introduce a way to enqueue view scripts only when needed for interactivity

Reported by: gziolo's profile gziolo Owned by:
Milestone: 6.7 Priority: normal
Severity: normal Version: 6.4
Component: Interactivity API Keywords: gutenberg-merge
Focuses: Cc:

Description

It's based on the discussion started by @felixarntz in https://github.com/WordPress/wordpress-develop/pull/5262#discussion_r1336258326 when reviewing the current handling for interactive blocks back ported from the Gutenberg plugin for WordPress 6.4 Beta 1.

As of today, the view scripts get enqueued during block rendering in WP_Block::render:

https://github.com/WordPress/wordpress-develop/blob/6a61084beca680a78ed581d035d76d592d4ef117/src/wp-includes/class-wp-block.php#L271-L274

The custom code used with all core blocks applies the optimization by ensuring that these view scripts are only listed for enqueuing when the block instance of a given type is going to need them. For example, in the Image block, the view script is only useful when the Lightbox feature is activated for the block. There can be multiple block instances of the same type, so the view script gets removed from the list when the functionality isn't used. However, as soon as any block instance needs it, it must be added and never removed.

The reason why we didn't go with wp_enqueue_script() and wp_dequeue_script() is that the script handle would still need to get removed from view_script_handles for the block type as it gets automatically registered through block.json and later enqueued in the code path shared in the snippet above. It would still have to be manually enqueued when it is needed.

In the long run, I hope we can develop some sort of automatic detection based on Interactivity API directives that would inform whether a view script is necessary for the block. It could even allow us to introduce new strategies that allow loading scripts based on some triggers like: when a user hovers over a UI element or when the UI element is visible on the screen.

Change History (19)

#1 @luisherranz
18 months ago

It could even allow us to introduce new strategies that allow loading scripts based on some triggers like: when a user hovers over a UI element or when the UI element is visible on the screen.

I started a discussion about lazy loading support a few months ago here:

https://github.com/WordPress/gutenberg/discussions/52723

I plan to work on all this after WP 6.4 is released.

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


14 months ago

#3 @chaion07
14 months ago

Thanks @gziolo for reporting this. We reviewed this ticket during a recent bug-scrub session. Although this ticket needs a patch and seeing no progress in recent time, we would request @luisherranz to look into this if possible. Cheers!

Props to @mukesh27 & @luisherranz

#4 @luisherranz
14 months ago

I haven't started exploring how to describe the assets required for an interactive block yet.

In my opinion, this is not a 1:1 relation (one block, one set of assets) like what we have now in block.json, but more of a 1:many relation where one block could have multiple interactive behaviors on the frontend, and the user should be able to choose and configure them in the UI. Third-party plugins should also be able to add new behaviors to existing blocks.

Related to this, @westonruter has started exploring how to lazy load interactive blocks:

Last edited 14 months ago by luisherranz (previous) (diff)

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


13 months ago

#6 @audrasjb
13 months ago

  • Milestone changed from 6.5 to 6.6

As per today's bug scrub. Since this ticket is still waiting for a patch proposal, moving it to the next milestone to give it more time.

#7 @oglekler
10 months ago

Hi @luisherranz could you please provide an update on the status of this ticket? We have about 2 weeks until Beta 1, even though Gutenberg is usually merged later, it's better to know if this feature will be in the release or not. Thank you!

#8 @luisherranz
10 months ago

As far as I know, no, but @gziolo might have more details.

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


10 months ago

#10 @nhrrob
10 months ago

  • Milestone changed from 6.6 to 6.7

We have reviewed in today's bug scrub.
Feedback from @hellofromTonya :
I'd suggest: Add the gutenberg-merge keyword, punt, and comment for the reason of punting, i.e. the patch is still in draft mode and hasn't yet been merged into or tested in Gutenberg. Also comment: if done in time, okay to put back into the milestone.

I am not seeing the gutenberg-merge keyword!

Punting to 6.7.

#11 @UmeshSingla
10 months ago

  • Keywords gutenberg-merge added

#12 @flixos90
7 months ago

@luisherranz @westonruter Not having much familiarity with the idea of lazy hydration myself, do you think https://github.com/WordPress/gutenberg/pull/58284 could be a solution to this ticket?

Are there blockers on that PR or is it just a matter of getting back to it?

#13 @luisherranz
7 months ago

do you think https://github.com/WordPress/gutenberg/pull/58284 could be a solution to this ticket?

I don't think so. Solving this ticket would require us to come up with a way to determine when a block has an interactive behavior and which assets (CSS, JavaScript) are associated with that behavior to enqueue them.

Are there blockers on that PR or is it just a matter of getting back to it?

That PR doesn't have any blockers, as far as I know.

#14 @westonruter
7 months ago

Should the scope here include not just the initial enqueueing of the dependent assets (scripts and styles) but also deferring the loading of them until they are needed? For example, the view script for the lightboxed Image block doesn't need to be loaded until the Image block is scrolled into the viewport.

This is where there is an intersection with lazy-hydration since the interactive island doesn't need to be initialized either until the block enters the viewport, the same as the scripts aren't needed until the block is in the viewport. As I understand, the process would be that once a block enters the viewport (1) download the scripts required by the block, and then (2) perform hydration.

This is also related to client-side navigation, as navigating to a page with an interactive block would also require its assets to be downloaded (but ideally not downloaded immediately but rather delayed until the block is in the viewport, especially for view script modules).

#15 @gziolo
6 months ago

Should the scope here include not just the initial enqueueing of the dependent assets (scripts and styles) but also deferring the loading of them until they are needed? For example, the view script for the lightboxed Image block doesn't need to be loaded until the Image block is scrolled into the viewport.

Yes, that sounds reasonable to look at it holistically, including all the ideas discussed in https://github.com/WordPress/gutenberg/discussions/52723. Overall, @westonruter you shared some good talking points that need to be taken into account.

Related to that, there is another GitHub issue where the topic of improved developer experience touches on the utils for enqueueing scripts and view scripts for blocks. We are now transitioning to script modules, so there might be similar need for view script modules.

One aspect that I was thinking about recently is how much we should inverse the control to offer an improved strategy, at least for Interactive API stores. There are now a few indicators in place that we could consider when optimizing how and when script modules get enqueued. To give some examples:

  • when there is no data-wp-interactivity attribute printed on the page then there is no need to load Interactivity API runtime
  • it's possible to detect which stores are necessary based on the directives used, example code:
    • $p->set_attribute( 'data-wp-interactive', 'core/image' );
    • $p->set_attribute( 'data-wp-on--click', 'core/query::actions.navigate' ); - this one is more complex as it requires a store from another block
  • everything custom, located in viewScriptModule files, that runs outside of the store probably needs different handling

So, an interesting path for exploration would be having a wholly revised strategy when the Interactivity API runtime loads automatically when there is any interactive region on the page. It would be responsible for deciding when to load individual stores that interactive islands need to operate as intended. To follow the example of the lightbox in the Image block, if the decision is to load that store when the block is in the viewport, then the runtime would handle everything, deferring loading the script module with core/image store. The only missing ingredient would be some sort of config that would allow to map the store name to the specific file on the disk so it can be dynamically imported and consumed by the runtime.

Last edited 6 months ago by gziolo (previous) (diff)

#16 @gziolo
6 months ago

  • Component changed from Editor to Interactivity API

#17 @westonruter
6 months ago

Also related is #61734 to potentially reduce the fetchpriority of loading the module scripts when not needed for critical rendering path.

#18 @gziolo
6 months ago

In 59083:

Build: Prepare for more Script Modules

This is a companion to https://github.com/WordPress/gutenberg/pull/65460 that requires syncing in WordPress Core. Namely, the block-library changes require registration with their updated script module IDs so that the blocks continue to work correctly.

They key improvement is script modules registration is handled in one central place, and a combined asset file is used to improve the performance by avoiding multiple disk operations for every individual file.

Props jonsurrell, gziolo, wildworks, noisysocks.
See #60647, #59462.

#19 @davidbaumwald
6 months ago

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

With 6.7 Beta 1 releasing shortly, I'm closing this ticket as Fixed. Any follow-up work can happen in new tickets. If there's additional iteration needed here specifically, this can be reopened as a Task (Blessed) in 6.8.

Note: See TracTickets for help on using tickets.