Opened 4 years ago
Last modified 4 weeks ago
#54018 reopened enhancement
Allow scripts registered via block.json to be enqueued in the footer
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | Future Release | Priority: | normal |
| Severity: | normal | Version: | |
| Component: | Script Loader | Keywords: | needs-refresh 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 (28)
This ticket was mentioned in PR #1639 on WordPress/wordpress-develop by donmhico.
4 years ago
#1
- Keywords has-patch added
#2
@
4 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
@
4 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
@
4 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
@
4 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() dataproperty can be used forwp_script_add_data()- Not in the example but passing a single object should probably be possible too
#6
@
4 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
@
4 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
@
3 years 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
@
3 years 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
@
3 years ago
Thanks @gziolo! The new strategy for loading blocks looks great - can't wait until added into core :-D
#11
@
3 years 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
@
2 years 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
@
2 years 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
@
2 years 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_headeven though they havein_footerset 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
@
2 years 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.
2 years ago
#18
in reply to:
↑ 15
@
2 years 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
deferbut they remain in theheadto 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?
#19
@
2 years ago
Yes, it can be closed. Using defer is now enabled by default to all view scripts that folks will register in block.json to use on the front end.
#20
@
2 years ago
- Keywords close removed
- Resolution set to fixed
- Status changed from new to closed
Thanks for confirming, @gziolo!
#21
@
3 months ago
- Keywords needs-patch added; has-patch removed
- Milestone changed from 6.4 to 6.9
- Resolution fixed deleted
- Status changed from closed to reopened
I'm re-opening this because I found that even with defer, scripts printed in the head are loaded with a high priority and compete with the loading of critical resources, like the LCP image. I've opened #63486 to propose allowing script modules to be printed in the footer (in block themes), and I've opened #61734 to introduce fetchpriority for scripts and script modules. However, for block view scripts registered via block.json, there should be a way to override the default priority and location for printing. I opened Gutenberg#71366 specifically for updating the schema for block.json for both in_footer and fetchpriority, but it may end up being redundant with this trac ticket.
This ticket was mentioned in Slack in #core by wildworks. View the logs.
6 weeks ago
#23
@
6 weeks ago
I wanted to note that the PR attached proposes two top-level properties in block.json:
enqueueEditorFooterenqueueScriptFooter
It doesn't cover view scripts and view script modules. It also doesn't consider the case that there might be multiple scripts per every type. It also doesn't take into account fetchpriority.
The comment from @ocean90 https://core.trac.wordpress.org/ticket/54018#comment:5 contains the best possible path forward as it allows targeting individual assets. In fact, we could mirror the same behavior in the .asset.php files so they contains all the details that is needed to optimize these assets, example:
<?php return array( 'handle' => 'my-editor-script', 'dependencies' => array('wp-dom-ready', 'wp-i18n'), 'version' => '11d702108040f841d934', 'fetchPriority' => 'low', 'inFooter' => true, );
The same details in block.json if you only want to override fetchPriority:
{ "editorScript": [ { "handle": "my-editor-script", "fetchPriority": "low" } ] }
#24
@
6 weeks ago
- Keywords needs-refresh has-patch added; needs-patch removed
This ticket was featured in today's 6.9 Bug Scrub. Based on this comment, the attached PR needs to be updated.
#25
@
5 weeks ago
I think we'll be able to land printing script modules in the footer (#63486) in 6.9, but updating block.json to support that I'm not sure about. I personally won't likely have time to work on that part in time for 6.9-beta1, but @b1ink0 may (as he's also been leading work on the modules-in-footer opt-in).
This ticket was mentioned in Slack in #core by westonruter. View the logs.
5 weeks ago
#27
@
4 weeks ago
@gziolo I have created a POC for handling this format:
"viewScript": [ "file:./view.js", { "handle": "test-block-script", "file": "file:./test.js", "dependencies": [ "jquery" ], "inFooter": true, "strategy": "async", "fetchpriority": "high", "version": "0.1.0" } ], "viewScriptModule": [ "file:./module.js", { "handle": "test-block-module", "file": "file:./test-module.js", "dependencies": [ "@wordpress/interactivity" ], "version": "0.1.0", "fetchpriority": "high" } ]
Below are the current minimal POCs:
https://github.com/b1ink0/wordpress-develop/commit/3a7f33d3ee944c3b43fca82c75efc9572b894db7
https://github.com/b1ink0/gutenberg/commit/be3406208eec0d5a1b3684505752ad828e89669b
To make this work, I had to patch the @wordpress/scripts package to support the new format. Since the build scripts don't expect an object inside the viewScript array, the build was failing. For local testing, I patched the @wordpress/scripts package directly inside node_modules.
This block.json format is based on this comment:
https://github.com/WordPress/gutenberg/issues/46954#issuecomment-1875279537
For this https://core.trac.wordpress.org/ticket/54018#comment:23 :
I'm not sure about the implementation when a handle is provided without a file. I tried directly modifying $wp_scripts as a potential solution, but since block registration is done in the init hook and wp_enqueue_scripts is fired later, $wp_scripts does not contain the enqueued scripts at that point.
The current behavior when only a handle is given is to return it directly, which will be enqueued at block render time.
https://github.com/WordPress/wordpress-develop/blob/ce984ff16c1084b6ee78b38d10f6d379fff6ffd5/src/wp-includes/class-wp-block.php#L604-L620
#28
@
4 weeks ago
- Milestone changed from 6.9 to Future Release
Thank you! Let's continue iterating on this for 7.0. For 6.9 we can at least have core default to registering scripts with fetchpriority=low (already done via #61734) and in_footer=true (in review via #63486). Having the block.json being able to override these defaults will be useful, but in the meantime this override can be done with PHP code after the blocks are registered.
This PR accepts the new boolean keys
enqueueEditorFooterandenqueueScriptFooterin$metadatato whether or not enqueue the script in footer.Usage
Just add
enqueueScriptFooter: trueif you want to enqueue thescriptin the footer and/orenqueueEditorFooter: truefor the editor-facing script in yourblock.json.Example
block.json{{{js
{
}
}}}
Trac ticket: https://core.trac.wordpress.org/ticket/54018