WordPress / performance

Performance plugin from the WordPress Performance Group, which is a collection of standalone performance modules.
https://wordpress.org/plugins/performance-lab/
GNU General Public License v2.0
364 stars 99 forks source link

Implement script loading strategies (async & defer) in core, themes, and plugins #786

Open westonruter opened 1 year ago

westonruter commented 1 year ago

Feature Description

Now that Script Loading Strategies (async and defer) have landed in WordPress 6.3 (see Core-12009 and Dev Note), the task now is to take advantage of this new performance capability in core, themes, and plugins. This is a tracking issue to that end.

6.4 Milestone

Future Release

Ecosystem

joemcgill commented 1 year ago

From today's bug scrub, is this something worth looking into? https://core.trac.wordpress.org/ticket/58810

felixarntz commented 1 year ago

@westonruter You have a point in the issue description on "Move jQuery and other dependencies to footer if there aren't any non-footer dependents?". While I would love to do this, I think unless we find a very clever solution (I can't think of at the moment), we can't reasonably do that, since jQuery is so heavily used on WordPress sites, in lots of unrecommended ways, like inline JS directly injected into e.g. post content that assumes that jQuery is loaded. So I think deferring jQuery will cause a ton of breakage, unless we find a reliable way to ensure it's not used anywhere on the page (even outside of the WP Scripts API, that's the important bit to consider additionally on this one).

westonruter commented 1 year ago

@felixarntz yeah, I had similar concerns when I added it to the list. It came to mind because I saw WooCommerce adding scripts to the footer, but since they depended on jQuery it got added to the head. I agree that it is not really feasible to do. I'll cross it out.

westonruter commented 1 year ago
  • Move theme scripts from footer to head and add defer.

I just tried this with Twenty Seventeen:

diff --git a/src/wp-content/themes/twentyseventeen/functions.php b/src/wp-content/themes/twentyseventeen/functions.php
index 83964158b1..a31adfcca9 100644
--- a/src/wp-content/themes/twentyseventeen/functions.php
+++ b/src/wp-content/themes/twentyseventeen/functions.php
@@ -447,6 +447,11 @@ add_action( 'wp_head', 'twentyseventeen_colors_css_wrap' );
  * Enqueues scripts and styles.
  */
 function twentyseventeen_scripts() {
+   $args = array(
+       'in_footer' => isset( $_GET['with-defer'] ) ? false : true,
+       'strategy' => isset( $_GET['with-defer'] ) ? 'defer' : null,
+   );
+
    // Add custom fonts, used in the main stylesheet.
    $font_version = ( 0 === strpos( (string) twentyseventeen_fonts_url(), get_template_directory_uri() . '/' ) ) ? '20230328' : null;
    wp_enqueue_style( 'twentyseventeen-fonts', twentyseventeen_fonts_url(), array(), $font_version );
@@ -479,14 +484,14 @@ function twentyseventeen_scripts() {
    // Skip-link fix is no longer enqueued by default.
    wp_register_script( 'twentyseventeen-skip-link-focus-fix', get_theme_file_uri( '/assets/js/skip-link-focus-fix.js' ), array(), '20161114', true );

-   wp_enqueue_script( 'twentyseventeen-global', get_theme_file_uri( '/assets/js/global.js' ), array( 'jquery' ), '20211130', true );
+   wp_enqueue_script( 'twentyseventeen-global', get_theme_file_uri( '/assets/js/global.js' ), array( 'jquery' ), '20211130', $args );

    $twentyseventeen_l10n = array(
        'quote' => twentyseventeen_get_svg( array( 'icon' => 'quote-right' ) ),
    );

    if ( has_nav_menu( 'top' ) ) {
-       wp_enqueue_script( 'twentyseventeen-navigation', get_theme_file_uri( '/assets/js/navigation.js' ), array( 'jquery' ), '20210122', true );
+       wp_enqueue_script( 'twentyseventeen-navigation', get_theme_file_uri( '/assets/js/navigation.js' ), array( 'jquery' ), '20210122', $args );
        $twentyseventeen_l10n['expand']   = __( 'Expand child menu', 'twentyseventeen' );
        $twentyseventeen_l10n['collapse'] = __( 'Collapse child menu', 'twentyseventeen' );
        $twentyseventeen_l10n['icon']     = twentyseventeen_get_svg(
@@ -499,7 +504,7 @@ function twentyseventeen_scripts() {

    wp_localize_script( 'twentyseventeen-global', 'twentyseventeenScreenReaderText', $twentyseventeen_l10n );

-   wp_enqueue_script( 'jquery-scrollto', get_theme_file_uri( '/assets/js/jquery.scrollTo.js' ), array( 'jquery' ), '2.1.3', true );
+   wp_enqueue_script( 'jquery-scrollto', get_theme_file_uri( '/assets/js/jquery.scrollTo.js' ), array( 'jquery' ), '2.1.3', $args );

    if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
        wp_enqueue_script( 'comment-reply' );

With SCRIPT_DEBUG disabled on my wordpress-develop environment, I tested:

$ npm run research -- benchmark-web-vitals -u http://localhost:8889/ -n 100 -o csv
> research
> ./cli/run.mjs benchmark-web-vitals -u http://localhost:8889/ -n 100 -o csv

URL,http://localhost:8889/
Success Rate,100%
FCP (median),82.95
LCP (median),82.95
TTFB (median),16.9
LCP-TTFB (median),65.15

And:

$ npm run research -- benchmark-web-vitals -u http://localhost:8889/?with-defer -n 100 -o csv
> research
> ./cli/run.mjs benchmark-web-vitals -u http://localhost:8889/?with-defer -n 100 -o csv

URL,http://localhost:8889/?with-defer
Success Rate,100%
FCP (median),94.3
LCP (median),94.3
TTFB (median),28.15
LCP-TTFB (median),65.25

So moving footer scripts to the head and making them defer is actually slightly worse for performance for Twenty Seventeen (~0.15%).

I also tried with Fast 3G network emulation:

$ npm run research -- benchmark-web-vitals -u http://localhost:8889/ -n 100 -o csv

> research
> ./cli/run.mjs benchmark-web-vitals -u http://localhost:8889/ -n 100 -c Fast 3G -o csv

URL,http://localhost:8889/
Success Rate,100%
FCP (median),744.2
LCP (median),744.4
TTFB (median),17.1
LCP-TTFB (median),728.2
$ npm run research -- benchmark-web-vitals -u http://localhost:8889/?with-defer -n 100 -o csv

> research
> ./cli/run.mjs benchmark-web-vitals -u http://localhost:8889/?with-defer -n 100 -c Fast 3G -o csv

URL,http://localhost:8889/?with-defer
Success Rate,100%
FCP (median),1339.6
LCP (median),1339.6
TTFB (median),614.6
LCP-TTFB (median),725

And here using defer is only 0.43% faster.

So it doesn't seem really worthwhile for Twenty Seventeen.

joemcgill commented 1 year ago

@felixarntz yeah, I had similar concerns when I added it to the list. It came to mind because I saw WooCommerce adding scripts to the footer, but since they depended on jQuery it got added to the head. I agree that it is not really feasible to do. I'll cross it out.

Just saw this question come up in WP Slack. I think it is worth us having a ticket to track this specific line of questions, even if we still determine that it's unfeasible for core, so individual sites can find direction about how to opt into supporting jquery as a deferred or footer loaded script. Also, we could better explain why it's not feasible to fix in core. @westonruter do you already have any shareable info we could add?

felixarntz commented 1 year ago

@westonruter Can you provide an update on the remaining points in the issue description where they're at?

Taking a glance, the first two remaining ones may still be subject to the 6.4 beta cutoff deadline, so maybe we should prioritize them? Are there tickets already, or what is left to do?

westonruter commented 1 year ago

@felixarntz:

Themes

In regards to themes, my findings for Twenty Seventeen are in https://github.com/WordPress/performance/issues/786#issuecomment-1681273989, and not finding any improvement to using async/defer in Twenty Seventeen kinda took the wind out of my sails. Nevertheless, all/both block themes (TT2 and TT3) have been accounted for by adopting defer for all block view scripts (Core-59115). I'll check the other themes and see if there are any clear wins.

Aside, current install counts for core themes:

Theme Count
2023 1,000,000+
2022 600,000+
2021 700,000+
2020 500,000+
2019 200,000+
2017 600,000+
2016 100,000+
2015 100,000+
2014 90,000+
2013 40,000+
2012 90,000+
2011 100,000+
2010 80,000+

Admin

I've filed Core-59301 to further investigate the admin. However, since admin pages are typically visited by a user multiple times they'll have the assets already in the browser cache, making optimization of the loading order less impactful than optimizing frontend responses for many first-time visits. Similarly, the admin would generally have a tiny fraction of the top level navigations on a given site compared to the frontend. For these reasons, I don't think we should prioritize too high the optimization of the admin until we're sure that the frontend has been fully optimized.

felixarntz commented 1 year ago

Thanks @westonruter.

Regarding the classic default themes, I wonder if we should still aim to make the changes just to set a good example on the loading strategies in general. I could see how they don't make a big difference in default themes because they don't typically load a ton of JS, but it may still set a good example, and other "heavier" themes may see a larger benefit. So I'm thinking as long as there aren't any adverse effects, it may still be good to raise a ticket for that to update default themes' frontend scripts to use the loading strategies. WDYT?

Regarding WP Admin, makes sense to me not to prioritize it too much. I guess we can keep that ticket open for now, though we might also argue based on your observations that this is not worth investing in at all and closing as wontfix or maybelater. Even then I think it's good to have opened this ticket just for historic purposes. 👍

westonruter commented 1 year ago

@felixarntz:

Regarding the classic default themes, I wonder if we should still aim to make the changes just to set a good example on the loading strategies in general. I could see how they don't make a big difference in default themes because they don't typically load a ton of JS, but it may still set a good example, and other "heavier" themes may see a larger benefit. So I'm thinking as long as there aren't any adverse effects, it may still be good to raise a ticket for that to update default themes' frontend scripts to use the loading strategies. WDYT?

Here's the ticket: Core-59316

And here's a PR which is ready for initial review: https://github.com/WordPress/wordpress-develop/pull/5170

westonruter commented 1 year ago
  • [ ] Find opportunities for async/defer scripts in the WP Admin (Core-59301).
  • [ ] Reach out to theme and plugin authors using legacy means of adding async & defer to adopt the new API.
  • [ ] Find new opportunities for using script loading strategies in popular themes & plugins.

Shall we split these out into separate issues and close this one as completed for 6.4? The first already has a Trac ticket.

felixarntz commented 1 year ago

@westonruter Since this is an overview issue, I think it's best to keep everything under here. I'd say we can remove the 6.4 milestone from this issue though, since not everything tracked by this issue is even subject to core releases.

joemcgill commented 1 year ago

On a few other overview issues, I've started adding separate headers for the stuff that is targeted for this release and for stuff that will land in a future release (example). Doing something similar here might be helpful, but I agree that it's best to keep everything related to this effort in one place rather than in separate issues that are milestone specific.