延迟无法更快地加载页面

时间:2019-01-31 10:17:51

标签: javascript deferred-loading

我试图通过一个简单的例子来理解defer。我有下面的JS代码,需要5秒钟才能运行。

home.js

function wait(ms){
    var start = new Date().getTime();
    var end = start;
    while(end < start + ms) {
      end = new Date().getTime();
   }

 }

 wait(5000);

我正在 home.html 中使用它,如下所示:

<html>
    <head>
        <script defer src="home.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

我发现,如果使用defer或将我的脚本放在body标签的末尾(据我所知,这是等效的),则hello world仅在5秒后出现。但是如果我使用asnyc,“ hello world”会立即出现。为什么会这样?

4 个答案:

答案 0 :(得分:1)

要回答您的迫切问题,有关MDN的Script Element文档将有所帮助。

推迟:

  

具有defer属性的脚本将阻止DOMContentLoaded   从触发到脚本加载完毕并完成评估为止的事件。

异步:

  

这是一个布尔属性,指示浏览器应   可能,异步加载脚本。

异步脚本不会阻止DOMContentLoaded事件触发。

有几种更好的方法可以使dom内容在延迟后出现,这些延迟不会像您所做的那样阻塞UI线程。这是非常糟糕的做法。

摘录自DOMContentLoaded

  

同步JavaScript暂停DOM的解析。如果您想要DOM   在用户请求后尽快解析   页面,您可以使JavaScript异步

通常的做法是在文档加载后添加dom内容。您仍然可以通过超时来执行此操作,但是通常会在其他asynchronous action之后加载其他dom内容,例如从fetch收到的响应。

以下是延迟后添加dom内容的示例:

function wait(ms){
  window.setTimeout(function() {
    var element = document.createElement("h1")
    element.innerText = "Hello World!"
    document.body.appendChild(element)
  }, ms)
}

 wait(5000);
<html>
    <head>
        <script src="home.js"></script>
    </head>
<body>
    
</body>
</html>

答案 1 :(得分:1)

让我们看一下(打开调试控制台以查看输出):

test.js

document.addEventListener("DOMContentLoaded", function(event) {
    console.log("[4]: 'DOMContentLoaded' event fired!");
});

function wait(ms){
    console.log("[2]: 'wait' started!");
    var start = new Date().getTime();
    var end = start;
    while(end < start + ms) {
        end = new Date().getTime();
    }
    console.log("[3]: 'wait' finished!");
}

wait(5000);

test.html

<html>
    <head>
        <script defer src="test.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
    <script>console.log('[1]: DOM is parsed!')</script>
</body>
</html>

控制台日志:

[1]: DOM is parsed!
[2]: 'wait' started!
[3]: 'wait' finished!
[4]: 'DOMContentLoaded' event fired!

现在您可以看到以下步骤:

  1. DOM已加载并解析。
  2. 运行wait函数(因为它是deferred,并且在解析DOM之后但在触发DOMContentLoaded事件之前运行)。
  3. 'defer`脚本完成了。
  4. DOMContentLoaded事件被触发(仅在所有defer脚本按照它们在HTML中的顺序执行之后)。
  5. 呈现了HTML,但是仍可以加载样式表和图像之类的某些资源。
  

在不同的浏览器中,DOM呈现时间可能有所不同,并且   托管HTML文件的方式:在Chrome(如我的测试所示)中,对于http://,DOM为   在运行defer脚本之前呈现,但是在file:///情况下   在运行defer脚本并完成其同步代码后呈现DOM。

摘要(来自您评论中的问题)

  • deferasync脚本是异步加载的。
  • defer脚本将按照它们在HTML中的放置顺序执行。
  • defer脚本将在加载和解析DOM之后但在触发DOMContentLoaded之前执行。
  • async脚本可以随时加载并以任何顺序运行(最有可能按照加载顺序)。如果在解析DOM之前恰好加载了这样的脚本,则不能指望该脚本能够找到HTML中的某些元素。另外,在这种情况下,浏览器不会等待脚本加载并执行以触发DOMContentLoaded事件。

答案 2 :(得分:1)

@Sergey的回答很好,但并没有真正解释。 4.12.1 here下的示意图显示,当指定defer时,浏览器将等待脚本执行开始,直到整个HTML被解析 。关键是解析不呈现。渲染与延期脚本的执行同时 开始,但是在这种特殊情况下,脚本陷入了沉重的循环。如果wait()函数未阻塞,则渲染将有机会在解析完成后和并行执行 后立即使用非阻塞的延迟脚本执行来有效地更新DOM。脚本完成后,仍然会触发DOMContentLoaded。

function wait(ms){
  return new Promise((resolve, reject) => {setTimeout(() => resolve(), ms)});
}

 wait(3000).then(() => console.log('done'));
<html>
    <head>
        <script defer src="home.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

答案 3 :(得分:0)

我不同意您的观察结论... defer不会使页面,如以下动画所示。请注意,页面开始渲染而无需等待DOM内容加载事件:

Page load animation

因此,您可以自由使用deferasync depending on your requirements

但是,根据页面是通过网络加载还是从文件系统加载,Chrome似乎在执行JavaScript之前或之后开始绘制页面:

网络:

Loaded over http:

文件系统:

Loaded over file:

虽然我不确定为什么Chrome选择采用这种方式,但我只能说这是您无法控制或依赖的。 defer旨在优化JavaScript内容在网络上的传递,其中大部分不适用于文件系统。