简体   繁体   English

当脚本被加载为“预加载”/“模块预加载”时会发生什么?

[英]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 ).在 web.dev 上有一篇非常有趣的文章,关于 module worker 的https://web.dev/module-workers/我们有能力将 worker 作为预加载模块加载,这意味着它们可以被预加载,甚至可以预解析和预取他们的依赖项( 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如果我是对的,不仅 Web-Workers 可以作为预加载模块加载,这适用于任何 js 脚本、字体、css 等。

<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?这是否意味着模块加载也会缓存解析后的代码,这意味着在主线程和工作线程中使用的模块将不会被再次解析,如果我们在顶部使用 import 语句包含它?

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相关文章: https : //developers.google.com/web/updates/2017/12/modulepreload#does_preloading_modules_help_performance

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如果我们检查规范, modulepreload链接算法的获取和处理链接资源算法步骤 5

  1. Let settings object be the link element's node document's relevant settings object .设置对象成为link元素的节点文档的相关设置对象

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 .然后在第 11 步将这个设置对象传递给fetch a modulepreload 模块脚本图算法,该算法本身将调用fetch a single module script获取 和 link 的后代- 最终也将调用fetch a single module script - 使用相同的设置对象

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 .moduleMap成为模块映射设置对象模块映射

  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.如果moduleMap[url]"fetching" ,则并行等待直到该条目的值发生变化,然后在网络任务源上将任务排队以继续运行以下步骤。

  3. If moduleMap[url] exists, asynchronously complete this algorithm with moduleMap[url] , and return.如果moduleMap[url]存在,则用moduleMap[url]异步完成该算法,并返回。

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.所以从那里我们可以看到modulepreload链接不仅会获取链接的资源,还会获取所有子资源,甚至为这些资源中的每一个准备模块脚本,这与本文所声称的非常吻合。

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 ".我们必须检查有关 专用 Workers 构造函数的规范,它将调用运行一个工作算法,传递我们的modulepreload链接正在使用的相同文档的对象设置,这次称为“外部设置”。

At step 8 of this run a worker algorithm, it asks to在第 8 步运行一个工作算法,它要求

  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.并且这个设置worker环境设置对象算法将只使用文档的外部设置来设置inherited origin内部值和新设置对象top-level origin属性。

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.因此,当工作器本身调用这些获取单个模块脚本获取其后代和链接算法作为获取模块工作器脚本图的一部分时,它将检查的一个模块映射是它自己的内部设置模块映射,然后,它不会找到我们的modulepreload链接创建的模块脚本。


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.因此,通过我对规范的阅读,我认为modulepreload链接仅对模块 Workers 有帮助,因为HTTP 缓存已经下载了图中的所有文件。 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.如果你打算只在 Worker 中使用这些模块,那么让它在文档端准备模块脚本实际上会适得其反,一个简单的prefetch链接可能会更好,除非你必须创建一个这样的链接每个子资源。

Kaiido Thank you for the detailed response. Kaiido谢谢你的详细回复。 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.我也在这个有问题的引用上搜索了很多,然后我在web.dev上发起了一个问题,如果他们可以更新内容。 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 JS 引擎最繁重的工作之一是解析/编译( 什么是解析/编译)我们的代码,它在及时与网页交互方面发挥着重要作用,而且我们可以通过多种方式改进 TTI,例如仅发送编写用户需要的代码(通过 webpack、块等进行代码拆分)、缩小、摇树、 http-cachingmodule-workers等......并且不要忘记预加载

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.一些资源隐藏在 CSS 和 JavaScript 中,浏览器无法知道它将需要它们,直到已经很晚。 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.但是 Preload 解决了这个问题。

I believe the author is trying to explain the same ie 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).我相信作者正在尝试使用链接 rel="modulepreload" 标签来解释相同的即预加载甚至预解析的模块(不执行+编译的字节码被缓存)以供以后评估(可以跳过重新编译)。

And Finally I believe my queries are resolved.最后,我相信我的疑问已解决。 Thank you so much!非常感谢!

Couple of Helpful Links:几个有用的链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM