Open dainemawer opened 2 years ago
@dainemawer I really like this idea. One thing that I will be very interested in is whether we can eventually make this work with assets that get registered via block.json
files also.
With WordPress moving more and more towards a block first approach more of the scripts and stylesheets will get automatically enqueued by WordPress through the block.json
files.
These registrations in block.json
files happens through the script
, viewScript
, style
, editorScript
and, editorStyle
keys. Each of these values need to be of the type WPDefinedAsset
Which means they can either be a string
that is a relative file path or which I think would be very interesting for this proposal an object containing properties like dependencies
, version
, handle
and src
. Maybe we can also add our own custom key-value pairs to that definition and then filter the actual registration to add the proposed strategy
to it.
{
"name": "example/block",
"viewScript": {
"src": "file:./view.js",
"handle": "example-block-view-script",
"strategy": "lazyOnLoad"
}
}
Just wanted to call this out as more and more of the registration of scripts is happening automatically abstracted away via the blocks.
@dainemawer, I think this idea is excellent, and I love the thought of being able to link block scripts in with these loading strategies @fabiankaegy!
I have a couple of thoughts.
Rather than replicating the way core enqueues files:
tenup_add_script( $handle = 'component', $filename = 'file-name', $ver = '1.0.0', $strategy = 'lazyOnLoad' );
I think we could future-proof it and have all the args after the handle and filename passed as an array. This would make it easy to extend in the future without us needing to keep adding additional arguments to the function. Something like:
tenup_add_script(
$handle = 'component',
$filename = 'file-name',
$args = array(
version => '1.0.0',
strategy => 'lazyOnLoad',
)
);
We'd also need to consider how this works with dependencies and whether we'd bypass them altogether. One of the powerful things we utilise in the scaffold currently is the @wordpress/dependency-extraction-webpack-plugin
package. That package simplifies the handling of dependencies for scripts massively. If we go down this route, then we'd need to find a way to handle those dependencies.
For example, if we defer a script using the afterInteractive
strategy, all those who depend on it must also be deferred. In the same thought, if we set a script to use the beforeInteractive
strategy, it'll be inlined, but what if that script depends on jQuery? Would this wrapper then be in charge of inlining jQuery into the page? How would that affect other scripts that relied on jQuery and used the core enqueue functionality? Would we throw a _doing_it_wrong()
error instead if there are dependencies passed to a script using the beforeInteractive
strategy?
I'm super excited about this; I want to make sure we think through those edge cases as they'll surely come up in a build.
While we are taking a look at the asset handling here I also want to call out this core API that is being used in blocks to make it possible to inline small assets: https://make.wordpress.org/core/2021/07/01/block-styles-loading-enhancements-in-wordpress-5-8/#inlining-small-assets
By providing a path
with the script (can be added later via wp_style_add_data( $style_handle, 'path', $file_path );
) you allow WordPress to know the file size of the asset and can allow it to inline small assets to reduce the amount of HTTP request for tiny files.
May be something that is worth considering for more than just blocks.
This is great feedback so far, thanks @fabiankaegy and @darylldoyle - I totally agree with both of your points!
jquery
as a dependency of moment
, and defer moment
and not jquery
- as long as jquery
loads first, the deferring is somewhat irrelevant.prefetch
/ preconnect
mechanism for core scripts like jQuery
.@darylldoyle I like the idea of using an array
. Related to point 3 if we could do something like:
tenup_add_script(
$handle = 'component',
$filename = 'file-name',
$args = array(
version => '1.0.0',
strategy => 'lazyOnLoad',
preconnect => true
)
);
That would also be a winner.
What are our next steps here?
I think object oriented programing is alien to the WordPress ecosystem. It needs an engineer to like it.
use TenUp\Utility\Script;
(new Script('url-of-javascript-file'))
->setHandle('component')
->setVersion('1.0.0')
->enableLazyLoading()
->enablePreconnect()
Is your enhancement related to a problem? Please describe.
Script loading in WordPress really sucks. We have a few options available to us:
<head>
</body>
script_loader_tag
to appendasync
anddefer
onto scripts.None of the options above, considering the WP ecosystem really has a huge effect on performance. I would like to suggest that we build our own function which sits on top of
wp_enqueue_script
, very much like NextJS handles theirnext/script
component: https://nextjs.org/docs/api-reference/next/scriptIn NextJS scripts can apply "strategies":
(from web.dev) The strategy attribute can take three values.
beforeInteractive
: This option may be used for critical scripts that should execute before the page becomes interactive. Next.js ensures that such scripts are injected into the initial HTML on the server and executed before other self-bundled JavaScript. Consent management, bot detection scripts, or helper libraries required to render critical content are good candidates for this strategy.afterInteractive
: This is the default strategy applied and is equivalent to loading a script with the defer attribute. It should be used for scripts that the browser can run after the page is interactive—for example, analytics scripts. Next.js injects these scripts on the client-side, and they run after the page is hydrated. Thus, unless otherwise specified, all third-party scripts defined using the Script component are deferred by Next.js, thereby providing a strong default.lazyOnload
: This option may be used to lazy-load low-priority scripts when the browser is idle. The functionality provided by such scripts is not required immediately after the page becomes interactive—for example, chat or social media plug-ins.As WP is not prerendered,
beforeInteractive
is null and void.We can however leverage
afterInteractive
andlazyOnLoad
as described above,afterInteractive
is effectively the equivalent of usingdefer
. What I would like to see is an option forlazyOnLoad
which loads scripts when the browsers main thread is idle usingrequestIdleCallback
- MDN. For browsers that do no supportrequestIdleCallback
there is a shim available.There is currently limited support in Safari for this functionality, but otherwise great support across the web. Id envision a function like:
I think a proof of concept could be worthwhile testing out to see how far we could get but would love to see something in action!
Designs
No response
Describe alternatives you've considered
No response
Code of Conduct