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
338 stars 90 forks source link

Partytown support #176

Open luisherranz opened 2 years ago

luisherranz commented 2 years ago

Description

I'm opening this issue to discuss how WordPress could take advantage of Partytown to improve its performance.

What is Partytown?

Partytown is a library to execute scripts inside a Web Worker and off the main thread. Its main goal is to increase the site's performance freeing the main thread by offloading non-critical scripts to a Web Worker. It acts as a bridge between the Web Worker and the DOM in a way that is 100% transparent to the executed script.

How does Partytown work?

Scripts use the type "text/partytown" to avoid running in the main thread. Partytown loads those scripts inside a Web Worker that has a proxied version of all the DOM APIs. When the script access a DOM API, the proxy intercepts it and uses one of these two implementations:

  1. If Atomics are available, Partytown uses them to communicate synchronously between the main and worker thread.
  2. If they are not, Partytown uses synchronous XHR requests intercepted by a Service Worker.

If neither Atomics nor Service Workers are available, Partytown just runs the scripts in the main thread.

It's probably worth noting that when a script executed inside a Partytown Web Worker adds a new script (using APIs like document.createElement('script')), that script is also added inside of the Web Worker and not leaked to the main thread.

More information about how it works in their official docs.

Partytown tradeoffs

  1. DOM operations are slower (and even throttled to reduce work in the main thread), so it should not be used for intensive UI scripts.
  2. Partytown fetches all the scripts using fetch(), so they need to have CORS headers (or they need to use a proxy).
  3. Events handled by scripts that call event.preventDefault() will have no effect.

More info about the tradeoffs in their official docs.

Restricted access to DOM APIs

Because scripts executed with Partytown are sandboxed and it controls the bridge, site owners can increase the security/control over those scripts by allowing or denying access to the DOM APIs on a per-API basis.

I don't think Partytown has an API to control this yet, but they have mentioned it several times.

Partytown Status

Partytown is still a young project. It moved from Alpha to Beta on 5th Jan 2022.

Goals

  1. Allow plugin developers to execute scripts inside a Partytown Web Worker.
  2. Allow site admins to select which scripts they want to execute inside a Partytown Web Worker.

Proposals

1. Add a worker-partytown strategy on wp_enqueue_script

Following @adamsilverstein proposal of a strategy parameter on wp_enqueue_script (#168), plugin developers could simply opt-in to execute any script in a Partytown Web Worker by using the worker-partytown strategy.

wp_enqueue_script(
  'non-critical-script',
  plugins_url( 'non-critical-script.js', __FILE__ ),
  array(),
  '1.0.0',
  'worker-partytown'
);

There could be other worker strategies as well, so I'd suggest using worker-partytown for this one. For example worker-dom for WorkerDOM or simply worker for a regular Web Worker.

With this API, plugins could start offering the Web Worker option to their users:

Web Worker option in plugins

We could expose a filter to allow plugins to configure Partytown:

function my_plugin_partytown_config( $config ) {
  $config["debug"] = true;
  return $config;
}
add_filter( 'partytown_configuration', 'my_plugin_partytown_config' );
<script>
  partytown = {
    debug: true,
  };
</script>

2. Partytown module/plugin

Create a module that exposes a user interface to let the site admins configure which services they want to execute inside a Partytown Web Worker.

We could add integrations one by one, making sure Partytown supports those services. If necessary, it should also take care of configuring the forwarded events.

Integrations could use regular expressions to find the scripts in the HTML and add the type="text/partytown" attribute to the script tag. For example, identify scripts with https://www.google-analytics.com/analytics.js and/or ga(...) for Google Analytics.

<!-- Loaded in Partytown Web Worker -->
<script type="text/partytown">
  (function (i, s, o, g, r, a, m) {
    /* ... */
  })(
    window,
    document,
    "script",
    "https://www.google-analytics.com/analytics.js",
    "ga"
  );
  ga("create", "", "auto");
  ga("send", "pageview");
</script>

If this module is successful, we could export its functionality to a Partytown plugin.

Service Worker integration

We would probably want to integrate it with the PWA plugin and only add the Service Worker directly if the PWA plugin is not present.

eclarke1 commented 2 years ago

@aristath @sgomes I have added this new issue to the [JavaScript] focus area, but please do let me know if this is not accurate. Added to Backlog for prioritisation.

adamsilverstein commented 2 years ago

Thanks for this proposal @luisherranz... cc: @westonruter who maintains the PWA plugin

thelovekesh commented 2 years ago

@luisherranz I would like to take this one. Can you please assign it to me? cc: @westonruter

luisherranz commented 2 years ago

Hey @thelovekesh, great to hear that πŸ™‚

I don't have permission to add assignees, but Weston or someone else will do it.

What approach are you going to follow?

thelovekesh commented 2 years ago

Sorry for the late reply @luisherranz.

I was working around an approach to show users an option, to enable Execute third-party scripts in Worker thread .

Once it enables, enqueue the party town script in the head. Now idea is to append type="text/partytown" in such scripts which have partytown in deps array.

wp_enqueue_script(
  'non-critical-script',
  plugins_url( 'non-critical-script.js', __FILE__ ),
  array('partytown'),
  '1.0.0'
);

Expected output:

// Loaded in Partytown Web Worker
<script type="text/partytown" src="/wp-content/plugins/labs/non-critical-script.js?ver=1.0.0"></script>

But appending the type attribute based on script dependency will be the best approach?


We could expose a filter to allow plugins to configure Partytown:

Related to exposing a filter to configure partytown, I agree with the approach suggested above πŸ‘πŸΌ

thelovekesh commented 2 years ago

I have created a POC for testing this functionality. Partytown Plugin - https://github.com/rtCamp/wp-partytown

Steps to use these plugins:

https://user-images.githubusercontent.com/54371619/159474078-d88fa7f7-293f-4347-90f4-4ab0d4881240.mp4

I have used the approach discussed in https://github.com/WordPress/performance/issues/176#issuecomment-1074735267

luisherranz commented 2 years ago

Wow! That's awesome Lovekesh. I've just tested and it works great πŸ˜„ πŸ‘


But appending the type attribute based on script dependency will be the best approach?

I don't know, to be honest. @adamsilverstein, what do you think? What alternatives do we have? (until we have something like https://github.com/WordPress/performance/issues/168).


@thelovekesh: Will you be willing to submit a PR to add it as a module so it can be tested by all the users of this plugin? That way it's also easier for other members of the Performance Team to contribute to the code πŸ™‚

thelovekesh commented 2 years ago

Wow! That's awesome Lovekesh. I've just tested and it works great smile clap

Thank you @luisherranz ! Glad you liked it.

Will you be willing to submit a PR to add it as a module so it can be tested by all the users of this plugin?

Of course, I will raise the PR ASAP probably by Monday so that team can release it in upcoming version very soon.

thelovekesh commented 2 years ago

@luisherranz I have raised the PR to add this as a module. Please have a look and let me know your thoughts.

alessandrocarrera commented 2 years ago

Hi @thelovekesh , awesome work! I tried locally and it works for the second and third test, but not for the first test (the only thats enqueue a script). When I click the button it not increment the counter. Furthermore, basing on the official doc (for the second and third tests), I see that the script changes in type="text/partytown-x" after the script runs but not in this case. With the second and third tests I see that the script type is always the same (before and after of the execution). Do you see a change of the script type after execute the script? Thank you again for your support 😊

alessandrocarrera commented 2 years ago

@thelovekesh I add that I tried it on a Wordpress clean installation (same theme of you and with only your plugin active). Thank you!

erip2 commented 1 year ago

I tried it too and I have the same result as @alessandrocarrera. Furthermore none of the scripts changed to type="text/partytown-x".

Is anything new about this and PR #271 ? :)

bethanylang commented 1 year ago

Thanks for testing, @alessandrocarrera and @erip2!

@thelovekesh Are you able to take a look?

thelovekesh commented 1 year ago

Hello @alessandrocarrera @erip2

If I am not mistaken, you have tried to work with the POC plugin . To enable partytown with that plugin you first have to allow partytown in the plugin's settings(Settings > Partytown > Enable PartyTown).

To avoid such confusions in future I will enable partytown setting in the POC plugin by default πŸ‘πŸΌ

image

@bethanylang I have tested and confirmed that functionally is fully working on both the POC plugin and raised PR.

erip2 commented 1 year ago

I did enable this option.

alessandrocarrera commented 1 year ago

I did enable this option.

@thelovekesh me, too.

thelovekesh commented 1 year ago

@erip2 @alessandrocarrera Can you try this on a fresh installation without the performance plugin? Also please check if it's enqueueing the partytown.js script with partytown handler.

Edit: Just for convenience, I have added @wordpress/env for a quick local environment. All you need is to run npm install && npm run wp-env start

erip2 commented 1 year ago

Trying in my theme, but now none of the examples work. Can confirm that the partytown.js is loaded.

The window.partytown returns:

{ "lib": "https://mytheme.test/app/plugins/wp-partytown-develop/includes/js/partytown/" }

I should note that I haven't installed the "Performance Lab" plugin.

Trying it in the plugin with @wordpress/env and the examples work. The counter example doesn't work on Firefox but works on Safari & Chrome.

thelovekesh commented 1 year ago

@erip2 I think in your case either the service worker is not registered correctly or is unable to intercept dom events. You can find a similar issue here - https://github.com/BuilderIO/partytown/issues/107

luisherranz commented 1 year ago

Are you testing this with https?

westonruter commented 1 year ago

I've just finished some research on the sandboxing capabilities of Partytown, which hadn't before been documented. I just blogged a writeup:Β Sandboxing with Partytown

luisherranz commented 1 year ago

Very interesting, Weston. Thanks for sharing.

Chiflaos commented 1 year ago

Hello, I'm new to this and I'm trying to optimize WordPress with the Partytown plugin. I have installed and activated it in the settings, I can see it in the source code, but it has not been applied to third-party scripts and my performance numbers in PageSpeed are still the same.

Mte90 commented 1 year ago

I saw now this ticket but an year ago I did an experimental plugin that just enable all the js registered for partytown in wordpress https://github.com/CodeAtCode/wp-partytown

Probably the only thing to do is update the assets that aren't updated since a while. So maybe can be interesting to have a dedicated plugin as it is too much effort for the performance plugin (in a previous PR there was some doubt about that is a beta project etc)?