Make WordPress Core

Opened 22 months ago

Last modified 6 weeks ago

#54018 new enhancement

Allow scripts registered via block.json to be enqueued in the footer

Reported by: jeremyfelt's profile jeremyfelt Owned by:
Milestone: Awaiting Review 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 (11)

This ticket was mentioned in PR #1639 on WordPress/wordpress-develop by donmhico.


22 months ago
#1

  • Keywords has-patch added

This PR accepts the new boolean keys enqueueEditorFooter and enqueueScriptFooter in $metadata to whether or not enqueue the script in footer.

Usage

Just add enqueueScriptFooter: true if you want to enqueue the script in the footer and/or enqueueEditorFooter: true for the editor-facing script in your block.json.

Example block.json

{{{js
{

"apiVersion": 2,
"name": "mico/block-example",
"title": "Mico Test Block",
"description": "Mico Test Block",
"attributes": {

"heading": {

"type": "string",
"default": ""

}

},
"textdomain": "mico",
"enqueueScriptFooter": true,
"script": "file:./build/script.js",
"enqueueEditorFooter": true,
"editorScript": "file:./build/index.js"

}

}}}

Trac ticket: https://core.trac.wordpress.org/ticket/54018

#2 @donmhico
22 months 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 @jeremyfelt
18 months 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 @gziolo
16 months 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.

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

#5 @ocean90
16 months 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 for wp_script_add_data()
  • Not in the example but passing a single object should probably be possible too
Last edited 16 months ago by ocean90 (previous) (diff)

#6 @gziolo
16 months 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 @gziolo
16 months 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)[].

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

#8 @belbo
3 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 @gziolo
3 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 @belbo
2 months ago

Thanks @gziolo! The new strategy for loading blocks looks great - can't wait until added into core :-D

#11 @westonruter
6 weeks 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.

Note: See TracTickets for help on using tickets.