Opened 3 years ago
Last modified 3 years ago
#54884 new enhancement
Load scripts conditionally to reduce page loading and processing time
Reported by: | galbaras | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | normal | Version: | |
Component: | Script Loader | Keywords: | |
Focuses: | javascript, performance | Cc: |
Description
There are some core scripts, like wp-polyfill
(+ regenerator-runtime
?) and jquery-migrate
, that are only needed on browsers lacking certain capabilities, yet these scripts are loaded on every page by default, whether the browser needs them or not.
Since mobile browsers typically have the required capabilities, it's particularly important to avoid loading unnecessary scripts on them. This will improve user experience and PageSpeed scores, without any downside.
Fortunately, browser capabilities can be detected with minimal JavaScript code, and only the necessary scripts added dynamically to the page using appendChild()
(or maybe by manipulating script tag attributes?).
There are also cases, such as wp-embed
, that are only needed when there is an object embedded on the page, which can be detected in PHP.
In both cases, there may be cases where the above scripts have dependencies that must be loaded, so both the respective front-end and back-end code must take dependencies into account.
Also, browser checks should be done early and requires scripts inserted so as to provide their functions to subsequent code that relies on it (not trivial, I know, but I trust the skills of the WordPress team/community).
Change History (10)
#3
@
3 years ago
Sources can still be minified, although not combined, and we certainly need to experiment a bit to find the impact anything we implement may have on a page. I just think we need to reduce the script load on as many pages as we can.
Would generating polyfills via core-js just move the code from one file to another?
#4
@
3 years ago
Depends on the webpack setup.
Polyfills are used to provide modern JS funcionality to older browsers.
For example - https://caniuse.com/?search=es7 is the browser feature set - you can see which minimum version of the browser supports array.prototype.includes.
Before doing any of the conditional loading, injecting polyfills, etc. I think it would best to agree on a minimum supported browser version. From there we can see which polyfills we would actually need.
Additionally, conditionally loading jQuery migrate can be a potential hurdle, because the main reason it's there is because theme and plugin authors use deprecated jQuery APIs.
To summarize:
1) Polyfills are used for browser backward compatibility
2) jQuery migrate is used for theme / plugin compatibility with newer versions of wordpress.
To achieve your end goal you'd need to load a detector JS file early in the page loading process, and it cannot be an asynchronous load or a deffered one. Script would need to block JS execution, do the detection and shim in the polyfill / jQuery migrate. That can and will break caching plugin, but a side-effect would be a slower and more complex page load.
To end on a high note - I think your proposal regarding wp-embed.js is awesome. WooCommerce already has conditionals to check if a website request is for the embed version
Constants::is_defined('IFRAME_REQUEST')
in order to prevent plugin updates when viewing embedded version of the site. The code and the detection can be backported to WP and only load the required embed JS / CSS when viewing the website from iframe.
#5
@
3 years ago
I totally agree about limiting browser support.
Admittedly, I'm not a front-end developer, and when starting this ticket, I was hoping to get better ideas from the people who are phenomenal at it. I use caching plugins everywhere, so a good solution for me will take them into account.
One way to do it is by laying the foundation for page optimisation plugins to combine some scripts in a special way, by adding script attributes or adding the scripts to the page within some wrapping (comment or special tag) that the plugins can recognise.
If this is done in a backward-compatible way, adoption can be gradual without any damage to other sites.
As for IFRAME_REQUEST
, can you please explain further how to use it to dequeue wp-embed
?
#6
@
3 years ago
OK, so turns out the wp-embed
converts links into embeds, which never happens on the front end... The following code seems to do the trick, but doing it in core is better, of course:
add_action( 'wp_footer', 'remove_wp_embed_script' ); function remove_wp_embed_script() { if ( ! defined( 'IFRAME_REQUEST' ) ) { wp_deregister_script( 'wp-embed' ); } }
2) jQuery migrate is used for theme / plugin compatibility with newer versions of wordpress.
This means that it should be possible to disable it per site, rather than load it "just in case" on sites that don't need it. See also (#37110 and) #51812.
Here's the code for this too:
add_action( 'wp_default_scripts', 'remove_jquery_migrate_script' ); function remove_jquery_migrate_script( $scripts ) { if ( ! is_admin() && isset( $scripts->registered['jquery'] ) ) { $script = $scripts->registered['jquery']; if ( $script->deps ) { $script->deps = array_diff( $script->deps, array( 'jquery-migrate' ) ); } } }
#7
@
3 years ago
I ran into very similar issues, there is so many extra scripts being loaded when you use WP packages: #55030.
#8
@
3 years ago
@SergeyBiryukov perhaps we should merge this ticket with #55030 and have a serious discussion about reducing the WordPress JS footprint? It would be good to handle core, as well as providing mechanisms for us by themes and plugins.
#9
@
3 years ago
Sure, this sounds like a good idea to propose to the Performance team to get more eyes on it.
Injecting scripts with appendChild can (and will) break minification and concatenation in caching plugins.
I think a better approach would be to either enforce a minimum browser version which will also have a side-effect of not caring about backwards compatibility or implement asset compiling and transpiling via webpack and browserlist file.
Second option can also generate all the needed polyfills via core-js module.