Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happen when a script is loaded as "preload"/"modulepreload"?

There is a very interesting article on web.dev on module worker's https://web.dev/module-workers/ where we have an ability to load worker's as preloaded module, which means they can be preloaded and even pre-parsed and prefetch their dependencies (https://web.dev/module-workers/#preload-workers-with-modulepreload).

If I am correct, not only Web-Workers can be loaded as preload module, this is applicable to any js script, font, css etc. like

<link rel="preload" href="fonts/cicle_fina-webfont.woff2" as="font" type="font/woff2" crossorigin="anonymous">

<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">

There is a saying in this article, which bothers me a lot:

Preloaded modules can also be used by both the main thread and module workers. This is useful for modules that are imported in both contexts, or in cases where it's not possible to know in advance whether a module will be used on the main thread or in a worker.

Does this mean that module loading also caches parsed code, which means modules that are used in the main thread and in a worker will not be parsed again, if we have included it using import statement on top?

However this does not happen, whenever we import module's on any realm (main thread, worker thread), they execute their import's independently and then future onward they refer to their parsed-cached instance's in their own realms.

I am really confused, what exactly is the author trying to explain. And how can we implement it.

Related articles: https://developers.google.com/web/updates/2017/12/modulepreload#does_preloading_modules_help_performance

like image 369
Dolly Avatar asked Sep 06 '20 05:09

Dolly


Video Answer


2 Answers

I am not sure where this article got that idea from, but reading the specs, I see no evidence that

Preloaded modules can also be used by both the main thread and module workers.


If we check the specs, the fetch and process the linked resource algorithm for modulepreload links algorithm does at step 5

  1. Let settings object be the link element's node document's relevant settings object.

This settings object is then passed at step 11 to the fetch a modulepreload module script graph algorithm, which will itself call fetch a single module script and fetch the descendants of and link — which will ultimately also call fetch a single module script — with that same settings object.

This settings object is where the module map will be found, and this module map will be used in fetch a single module script to avoid requesting multiple times the same module (a cache).

  1. Let moduleMap be module map settings object's module map.

  2. If moduleMap[url] is "fetching", wait in parallel until that entry's value changes, then queue a task on the networking task source to proceed with running the following steps.

  3. If moduleMap[url] exists, asynchronously complete this algorithm with moduleMap[url], and return.

It should probably be noted that while this algorithm creates a module script, it doesn't execute it yet.


So from there we can see that the modulepreload links will not only fetch the linked resource, but all sub-resources and even prepare module scripts for each of these resources, which corresponds a lot to what this article claims otherwise.

However, this is not enough to conclude anything regarding the problematic quote.

We have to go check the specs about dedicated Workers constructor, which will call the run a worker algorithm passing the same document's object setting that our modulepreload link was using, this time called "outside settings".

At step 8 of this run a worker algorithm, it asks to

  1. Set up a worker environment settings object with realm execution context and outside settings, and let inside settings be the result.

And this set up a worker environment settings object algorithm will only use the document's outside settings to set the inherited origin internal value and the new settings object's top-level origin property.

This new settings object's module map is the one of its global scope, which is "initially empty".

The worker's environment settings object doesn't inherit its module map from outside settings.

So when the worker will itself call these fetch a single module script and fetch the descendants of and link algorithms as part of fetch a module worker script graph, the one module map it will check is its own inside settings's module map and there, it won't find the module scripts our modulepreload link has created.


So by my reading of the specs, I'd say that modulepreload links only help for module Workers is that the HTTP cache will already have downloaded all the files in the graph. If you are going to use these modules only in the Worker, then having it prepare the module scripts on the document's side is actually counter-productive, and a simple prefetch link might do better, except that you'd have to create one such link per sub-resource.

like image 185
Kaiido Avatar answered Oct 14 '22 05:10

Kaiido


Kaiido Thank you for the detailed response. It is very helpful.

I also searched a lot on this problematic quote and then I initiated an issue on web.dev if they could update the content. You can track on Issue

One of heaviest job for JS Engine is to parse/compile(What is Parse/Compile) our code and it plays an important role in time to interact of a web page and also there are many ways where we can improve TTI like only sending the code a user needs(code-splitting by webpack, chunks etc..), minification, tree shaking, http-caching, module-workers etc.... and also not to forget preload

Actual Definition of Preload:

The basic way you could use preload is to load late-discovered resources early. While most markup-based resources are discovered fairly early by the browser’s preloader, not all resources are markup-based. Some of the resources are hidden in CSS and in JavaScript, and the browser cannot know that it is going to need them until it is already fairly late. So in many cases, these resources end up delaying the first render, the rendering of text, or loading of critical parts of the page.

In short:

Download a resource(preparse + compile) because you know you’d need it, but you don’t yet want to execute it.

Earlier, If we inject the script at the point we want it to run, the browser will have to then download the script before it can be executed(NOTE: Browser has to do lot of stuff before executing your code), which can take a while. But Preload resolve this.

I believe the author is trying to explain the same i.e preloaded and even pre-parsed modules(Not to execute + compiled bytecode is cached) using link rel="modulepreload" Tag for later evaluation(re-compilation can be skipped).

And Finally I believe my queries are resolved. Thank you so much!

Couple of Helpful Links:

  • https://developers.google.com/web/updates/2017/12/modulepreload
  • https://3perf.com/blog/link-rels/
like image 31
Dolly Avatar answered Oct 14 '22 06:10

Dolly