-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Add support for printing script modules in footer #9867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for printing script modules in footer #9867
Conversation
…footer printing control
|
Hi @b1ink0! 👋 Thank you for your contribution to WordPress! 💖 It looks like this is your first pull request to No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making. More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook. Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook. If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook. The Developer Hub also documents the various coding standards that are followed:
Thank you, |
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
…cking Co-authored-by: Weston Ruter <[email protected]>
… in footer Co-authored-by: Weston Ruter <[email protected]>
| // If any dependency is set to be printed in footer, skip printing this module in head. | ||
| $dependencies = $this->get_dependencies( array( $id ) ); | ||
| foreach ( $dependencies as $dependency ) { | ||
| if ( $dependency['enqueue'] && $dependency['in_footer'] ) { | ||
| continue 2; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this right? Should they not both be printed in the head instead?
Consider classic scripts:
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_script( 'foo-footer', '/footer.js', array(), false, array( 'in_footer' => true ) );
wp_enqueue_script( 'bar-head', '/head.js', array( 'foo-footer' ), false, array( 'in_footer' => false ) );
} );This results in the following being printed in the head:
<script src="http://localhost:8000/footer.js?ver=6.9-alpha-60093-src" id="foo-footer-js"></script>
<script src="http://localhost:8000/head.js?ver=6.9-alpha-60093-src" id="bar-head-js"></script>Nevertheless, for script modules since they are always deferred to execute until after the DOM has loaded, whether they are in the head or footer makes little difference.
I just want to make sure that we're intentional about this. Or if it makes more sense to align the behavior with classic scripts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there is a head module which depends on a footer module? This is accounted for in
WP_Scripts.
I think I made a mistake the current logic is the reverse of what happens with classic scripts.
The only difference when script modules are in the footer is that the download starts later compared to those in the head, but I'm not sure if that's good or bad. If we don't care about the later download, then it makes sense to align the behavior with classic scripts.
Is there any benefit to moving the dependent to the footer if its dependency is in the footer for script modules that I might be missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a similar question to #9770 with fetchpriority. If foo has a has a high priority, and its dependency bar has a low priority. What is being sorted out there is whether the priority of bar establishes the priority of foo when it is enqueued (if foo was not directly enqueued as well). So if bar is enqueued, then a modulepreload link for foo is added with fetchpriority=low because bar is low.
In the same way, let's say you register a script module bar to be printed in the HEAD, but it has a dependency foo which is supposed to print in the footer. If you just enqueue bar, then a modulepreload link for foo could be added to the HEAD along with the SCRIPT for bar.
However, if you enqueue both foo and bar, then we have to figure out whether to move bar to the the footer to go along with foo, or we move foo to go into the HEAD along with bar. The order matters because it's possible they are not behaving as pure modules and could be interacting with globals.
In reality, this is probably very much an edge case that we shouldn't stress about. Since script modules do not execute until the DOM has loaded anyway, it doesn't really matter where they are located from a functional perspective (ahem, unless async is added in which they might, but we support these, and the order is even less important in this case).
cc @sirreal for his thoughts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested and thought about this a lot. I think the safest thing should be done, which is that later wins. If a module in the head depends on a module in the footer, they should move to the footer. If a module in the footer depends on a module in the head, it doesn't matter they can both stay where they declared.
This comes from an assumption that it's more likely for modules to expect DOM to be loaded than to expect the DOM not to be loaded. It's also closer to the standard non-async behavior of modules, in case an async module imports a non-async module that expect to load after the DOM.
less organized, rambling thoughts
The order matters because it's possible they are not behaving as pure modules and could be interacting with globals.
It seems practically malicious for modules to rely on a particular script tag ordering. I'm reluctant even consider that as behavior to support. I've managed to create an error situation like is described here, but it wasn't easy. The only way that occurred to me to trigger this problem was to put an async module at the top of a document that imported a non-async module that expected to load after the DOM. This already suggests what the solution should be (later wins).
I'll share some assumptions and beliefs I have about modules:
- Modules always load after the DOM unless they're
async. - Modules are easy to reason about unless they're
async. - Modules in WordPress are not
async. It's possible to make themasync, but by default they are not. - Trying to anticipate and support the mess that
asyncmodules can make is likely to be frustrating and may ultimately be impossible. - Dependency and enqueued modules tend to be different.
- Enqueued modules are entry points that do things on their own, they have side effects of booting up functionality when they're evaluated. Usually this is via a
<script>tag, but they can be imported. - It's unusual for enqueued modules to export things, so it's unusual for enqueued modules to depend on other enqueued modules. A possible exception is when a module depends on the side effect of another module.
- Dependency modules can and will have side effects. These may expected to load after the DOM.
Co-authored-by: Weston Ruter <[email protected]>
Co-authored-by: Weston Ruter <[email protected]>
Co-authored-by: Weston Ruter <[email protected]>
|
Merge conflict resolved. |
…into enhancement/63486-script-modules-footer-support
Co-authored-by: Weston Ruter <[email protected]>
Co-authored-by: John Parris <[email protected]>
Co-authored-by: John Parris <[email protected]>
Yes, I think so. I just wanted to highlight whether we want to handle this case for the script module. |
peterwilsoncc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good, I'm about to do some manual testing.
In the new and modified tests, are you able to add an @ticket 63486 annotation for ease of testing at a later date?
Co-authored-by: Peter Wilson <[email protected]>
…into enhancement/63486-script-modules-footer-support
Co-authored-by: Peter Wilson <[email protected]>
…tion API in some plugins This reverts commit 9915940. Co-authored-by: Peter Wilson <[email protected]>
Co-authored-by: Peter Wilson <[email protected]>
Co-authored-by: Peter Wilson <[email protected]>
Co-authored-by: Peter Wilson <[email protected]>
Co-authored-by: Peter Wilson <[email protected]>
peterwilsoncc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm done with my various nitpicks and notes and it's working as expected based on my manual testing.
👍
This brings API parity with `WP_Scripts` by implementing opt-in support for printing in the footer via an `in_footer` argument. This argument can be supplied via the `$args` array passed to `wp_enqueue_script_module()` or `wp_register_script_module()`, alongside the existing `fetchpriority` key introduced in #61734. It can also be set for previously-registered script modules via `WP_Script_Modules::set_in_footer()`. This is not applicable to classic themes since modules are enqueued while blocks are rendered after `wp_head` has completed, so all script modules are printed in the footer anyway; the `importmap` script must be printed after all script modules have been enqueued. Script modules used for interactive blocks (with the Interactivity API) are automatically printed in the footer. Such script modules should be deprioritized because they are not in the critical rendering path due to interactive blocks using server-side rendering. Script modules remain printed at `wp_head` by default, although this default should be revisited since they have deferred execution (and they are printed in the footer for classic themes already, as previously noted). Moving a script module to the footer ensures that its loading does not contend with the loading of critical resources, such as the LCP element's image resource, and LCP is improved as a result. This also improves specificity of some PHP types, it ensures that script modules can't be registered with an empty ID, and it prevents printing script modules with empty `src` URLs. Developed in #9867 Follow-up to [60704]. Props b1ink0, westonruter, jonsurrell, peterwilsoncc, vipulpatil, mindctrl. See #61734. Fixes #63486. git-svn-id: https://develop.svn.wordpress.org/trunk@60999 602fd350-edb4-49c9-b593-d223f7449a82
This brings API parity with `WP_Scripts` by implementing opt-in support for printing in the footer via an `in_footer` argument. This argument can be supplied via the `$args` array passed to `wp_enqueue_script_module()` or `wp_register_script_module()`, alongside the existing `fetchpriority` key introduced in #61734. It can also be set for previously-registered script modules via `WP_Script_Modules::set_in_footer()`. This is not applicable to classic themes since modules are enqueued while blocks are rendered after `wp_head` has completed, so all script modules are printed in the footer anyway; the `importmap` script must be printed after all script modules have been enqueued. Script modules used for interactive blocks (with the Interactivity API) are automatically printed in the footer. Such script modules should be deprioritized because they are not in the critical rendering path due to interactive blocks using server-side rendering. Script modules remain printed at `wp_head` by default, although this default should be revisited since they have deferred execution (and they are printed in the footer for classic themes already, as previously noted). Moving a script module to the footer ensures that its loading does not contend with the loading of critical resources, such as the LCP element's image resource, and LCP is improved as a result. This also improves specificity of some PHP types, it ensures that script modules can't be registered with an empty ID, and it prevents printing script modules with empty `src` URLs. Developed in WordPress/wordpress-develop#9867 Follow-up to [60704]. Props b1ink0, westonruter, jonsurrell, peterwilsoncc, vipulpatil, mindctrl. See #61734. Fixes #63486. Built from https://develop.svn.wordpress.org/trunk@60999 git-svn-id: http://core.svn.wordpress.org/trunk@60335 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This brings API parity with `WP_Scripts` by implementing opt-in support for printing in the footer via an `in_footer` argument. This argument can be supplied via the `$args` array passed to `wp_enqueue_script_module()` or `wp_register_script_module()`, alongside the existing `fetchpriority` key introduced in #61734. It can also be set for previously-registered script modules via `WP_Script_Modules::set_in_footer()`. This is not applicable to classic themes since modules are enqueued while blocks are rendered after `wp_head` has completed, so all script modules are printed in the footer anyway; the `importmap` script must be printed after all script modules have been enqueued. Script modules used for interactive blocks (with the Interactivity API) are automatically printed in the footer. Such script modules should be deprioritized because they are not in the critical rendering path due to interactive blocks using server-side rendering. Script modules remain printed at `wp_head` by default, although this default should be revisited since they have deferred execution (and they are printed in the footer for classic themes already, as previously noted). Moving a script module to the footer ensures that its loading does not contend with the loading of critical resources, such as the LCP element's image resource, and LCP is improved as a result. This also improves specificity of some PHP types, it ensures that script modules can't be registered with an empty ID, and it prevents printing script modules with empty `src` URLs. Developed in WordPress/wordpress-develop#9867 Follow-up to [60704]. Props b1ink0, westonruter, jonsurrell, peterwilsoncc, vipulpatil, mindctrl. See #61734. Fixes #63486. Built from https://develop.svn.wordpress.org/trunk@60999 git-svn-id: https://core.svn.wordpress.org/trunk@60335 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This pull request introduces support for printing script modules in the footer.
Trac ticket: https://core.trac.wordpress.org/ticket/63486
Companion Gutenberg PR for upstream changes: WordPress/gutenberg#72459
TODO:
Print(Maybelater)modulepreloadlinks for footer script modules also in the footer.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.