Opened 3 years ago
Closed 14 months ago
#54018 closed enhancement (fixed)
Allow scripts registered via block.json to be enqueued in the footer
Reported by: | jeremyfelt | Owned by: | |
---|---|---|---|
Milestone: | 6.4 | Priority: | normal |
Severity: | normal | Version: | |
Component: | Script Loader | Keywords: | has-patch |
Focuses: | performance | Cc: |
Description
It should be possible to pass an extra argument to register_block_script_handle()
via register_block_type_from_metadata()
that registers a block's front-end script so that it is enqueued in the footer.
OR It should be possible for this information to be provided in block.json
with the script
property.
OR It should be possible to filter the wp_register_script()
call in register_block_script_handle()
so that $footer = true
can be passed just in time based on the handle of the script being registered.
I'm probably missing a possibility, and I'm not sure what approach would be best, but I would like to output front-end scripts for blocks at the end of the page.
Change History (20)
This ticket was mentioned in PR #1639 on WordPress/wordpress-develop by donmhico.
3 years ago
#1
- Keywords has-patch added
#2
@
3 years ago
Hello @jeremyfelt,
I've attached a PR allows which what you asked by adding support to enqueueScriptFooter
and enqueueEditorFooter
fields in $metadata
. I've thought to add the support for the editor script as well. You can see an example usage in the PR.
Let me know your thoughts.
#3
@
3 years ago
- Component changed from General to Script Loader
- Focuses performance added
Thanks for the patch, @donmhico! I'm not in a position to review right now, but I'm going to tag it as performance with the hope that a maintainer will take a look.
#4
@
3 years ago
It’s worth noting that @adamsilverstein has just opened a proposal to extend script loading strategies in https://github.com/WordPress/performance/issues/168. Let’s make sure that the solution proposed takes that into account.
Another consideration is support for multiple scripts per type in block.json as discussed in https://github.com/WordPress/gutenberg/issues/33542. @aristath started working on that already. We already allow passing an array for styles. The same is planned for all 3 types of scripts.
#5
@
3 years ago
I'd love to see this possible, although I think it should have been in the footer by default.
Instead of having a single property I have thought about extending the properties to allow an array of objects. An example:
{ "editorScript": [ { "path": "file:editor1.js", "footer": true, "data": { "async": true } }, "file:editor2.js", "my-editor-script-handle" ], "editorStyle": [ { "path": "file:editor1.css", "media": "print" }, { "path": "file:editor2.css", "media": "(max-width: 640px)" }, "file:editor3.css" ], "style": "file:style-blocks.css", "viewScript": [ "my-block-view-handle", "my-block-view-2-handle" ] }
- For back-compat it still allows passing only one handle or file
- You can pass an array of handles and/or file
- You can pass an array of objects with settings passed to
wp_register_style()
/wp_register_script()
data
property can be used forwp_script_add_data()
- Not in the example but passing a single object should probably be possible too
#6
@
3 years ago
I've attached a PR allows which what you asked by adding support to enqueueScriptFooter and enqueueEditorFooter fields in $metadata. I've thought to add the support for the editor script as well. You can see an example usage in the PR.
@donmhico, thank you for the patch proposed. I checked the code and it might not be the best fit once we allow multiple scripts per type:
"editorScript": [ "file:./editor-1.js", "file:./editor-2", 'block-editor-script-3" ],
In theory, you could also allow array for newly proposed field in block.json
:
"editorScriptFooter": [ true, false, null ],
However, the issue is that you can pass both paths to the asset file, but you can also pass the registered script handle like block-editor-script-3
where you would already have that flag configured.
@jeremyfelt, one solution that works today is to register the script separately and pass the handle to block.json
file. The idea was to both cover some already registered scripts, but also to offer more control on how those scripts might get registered.
OR It should be possible to filter the wp_register_script() call in register_block_script_handle() so that $footer = true can be passed just in time based on the handle of the script being registered.
This is also an option worth considering. If we do it, we should probably add a similar filter for register_block_style
.
#7
@
3 years ago
I saw a comment from @ocean90 and I like this proposal. I was about to write that we could consider magic code comments in JavaScript files that would allow the build tool to extract that extra information. However, the solution described by Dominik looks way much better.
When using file:editor1.js
format you still would need a build step that generates the dependencies and the version, but now you would be able to pass extra arguments when necessary or even override some of those automatically generated if really necessary.
Not in the example but passing a single object should probably be possible too
I'm not sure if we really need it because you could make it as simple as:
"editorScript": [ { "path": "file:abc.js", "data": { "async": true } } ]
Anyway, it's the least important part here. The string format would be mostly for backward compatibility so it would be simpler to have a string or array for the field: string|(string|object)[]
.
#8
@
19 months ago
@gziolo is this still the best way of doing this? I tried your suggestion of passing an already registered script handle, which worked but then WP scripts no-longer compiles the script I wanted to include.
For anyone else looking for a solution this is how to do it (unless @gziolo comes back with a better way as this ticket is quite old, however it is the first to show up on Google!)
In block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "prefix/blockname"
...
"viewScript": [ "carousel-fe", "file:./carousel.js" ] enqueue the same file twice
}
in functions.php
function enqueue_fe_scripts() { // Dequeue script loaded by gutenberg so script not added twice wp_dequeue_script( 'prefix-blockname-view-script-2' ); // include build script so can get dependencies, build version etc $carousel_asset = include PATH_TO_FILE . '/index.asset.php'; // Register but don't enqueue the script, as Gutenberg will enqueue it through viewScript wp_register_script( 'carousel-fe', PATH_TO_FILE_URI/carousel.js, array(), $carousel_asset['version'], true ); } add_action( 'wp_enqueue_scripts', 'enqueue_fe_scripts', 30 );
As far as I can tell this is the only way of getting WP scripts to compile the file to include on the Front End AND include it in the footer.
#9
@
19 months ago
We now track the issue to improve loading strategies for block assets in the GitHub repository at https://github.com/WordPress/gutenberg/issues/46954. There are also ongoing efforts from the WordPress Performance team to introduce more options to load scripts: https://make.wordpress.org/core/2022/12/09/enhancing-the-scripts-api-with-a-loading-strategy/.
@belbo, you are correct, I didn't think about the challenges with the default behavior of @wordpress/scripts
. We definitely need a better developer experience here.
#10
@
19 months ago
Thanks @gziolo! The new strategy for loading blocks looks great - can't wait until added into core :-D
#11
@
18 months ago
I just discovered that because the File block's view script is loaded in the head
(as other blocks' scripts are), it is currently failing hide PDF embeds for unsupported browsers because it doesn't wait to do so until the page loads. I opened a PR to fix. Naturally this issue wouldn't have happened if the script were loaded in the footer, and maybe it used to be in the footer and this is a regression.
#12
@
15 months ago
Now that script loading strategies (async & defer) are landing finally in 6.3 (#12009), the scope of this ticket should expand to not only allow block view scripts to be printed in the footer but also to get async/defer. Ideally block view scripts would be defer by default (which I understand is also the vision of the Interactivity API via modules).
#13
@
15 months ago
- Milestone changed from Awaiting Review to 6.4
Are there any cases where a block view script has to be executed in the head
? If not, instead of moving block view scripts to execute in the footer, as has been suggested on gutenberg#52536, how about keeping them in the head
but add the defer
attribute? This will allow early discovery of the block scripts to start loading early, but then their execution will still be delayed until the DOM has loaded, thus not impeding LCP.
#14
@
15 months ago
As I just commented on the aforementioned Gutenberg PR, we actually _can_ go ahead and mark all view scripts as defer
(as opposed to in_footer:true
) because they are printed in the footer already for classic themes:
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 havein_footer
set tofalse
. For block themes, however, parsing is done before template rendering, which means any enqueued block view script will get printed atwp_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.
#15
follow-up:
↓ 18
@
15 months ago
I think this can be closed once gutenberg#52536 is merged and backported to core. With it, all block view scripts are marked as defer
but they remain in the head
to promote early discovery. I don't think we need to provide a way to further customize how the block view scripts are printed via block.json
.
This ticket was mentioned in Slack in #core-performance by joemcgill. View the logs.
14 months ago
#18
in reply to:
↑ 15
@
14 months ago
- Keywords close added
Replying to westonruter:
I think this can be closed once gutenberg#52536 is merged and backported to core. With it, all block view scripts are marked as
defer
but they remain in thehead
to promote early discovery. I don't think we need to provide a way to further customize how the block view scripts are printed viablock.json
.
Based on @westonruter's comment. I think this can now be closed as fixed. @gziolo can you confirm?
This PR accepts the new boolean keys
enqueueEditorFooter
andenqueueScriptFooter
in$metadata
to whether or not enqueue the script in footer.Usage
Just add
enqueueScriptFooter: true
if you want to enqueue thescript
in the footer and/orenqueueEditorFooter: true
for the editor-facing script in yourblock.json
.Example
block.json
{{{js
{
}
}}}
Trac ticket: https://core.trac.wordpress.org/ticket/54018