网页的加载和执行顺序?


244

我已经完成了一些基于Web的项目,但是对于普通网页的加载和执行顺序,我考虑得并不多。但是现在我需要知道细节。很难从Google或SO找到答案,因此我创建了这个问题。

示例页面如下:

<html>
 <head>
  <script src="jquery.js" type="text/javascript"></script>
  <script src="abc.js" type="text/javascript">
  </script>
  <link rel="stylesheets" type="text/css" href="abc.css"></link>
  <style>h2{font-wight:bold;}</style>
  <script>
  $(document).ready(function(){
     $("#img").attr("src", "kkk.png");
  });
 </script>
 </head>
 <body>
    <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
    <script src="kkk.js" type="text/javascript"></script>
 </body>
</html>

所以这是我的问题:

  1. 该页面如何加载?
  2. 加载的顺序是什么?
  3. 何时执行JS代码?(内联和外联)
  4. CSS何时执行(应用)?
  5. $(document).ready什么时候执行?
  6. 会下载abc.jpg吗?还是只是下载kkk.png?

我有以下理解:

  1. 浏览器首先加载html(DOM)。
  2. 浏览器开始逐行从上到下加载外部资源。
  3. 如果一个 <script>满足a,将阻止加载并等待,直到加载并执行JS文件,然后继续。
  4. 其他资源(CSS /图像)并行加载并在需要时执行(例如CSS)。

还是这样的:

浏览器解析html(DOM),并以数组或类似堆栈的结构获取外部资源。加载html后,浏览器开始并行加载结构中的外部资源并执行,直到所有资源都加载完毕。然后,根据JS,将根据用户的行为更改DOM。

任何人都可以详细说明当您得到HTML页面的响应时会发生什么情况?这在不同的浏览器中是否有所不同?关于这个问题有参考吗?

谢谢。

编辑:

我在Firefox中使用Firebug进行了实验。如下图所示: 替代文字


11
史蒂夫·索德斯(Steve Souders)在这一领域做了大量工作。Google for steve + souders + high + performance来看看。
anddoutoi

3
我不是说性能调整。我想知道细节。
朱涛

2
通过阅读他的作品,我对“它”如何详细工作的理解增加了十倍,因此仍然是有效的评论。著作权不允许我在这里引用他的整本书,因此我仍然建议您仔细阅读他的著作。
anddoutoi

3
事情发生的顺序的完整描述在这里
Gerrat 2015年

Answers:


277

根据您的样本,

<html>
 <head>
  <script src="jquery.js" type="text/javascript"></script>
  <script src="abc.js" type="text/javascript">
  </script>
  <link rel="stylesheets" type="text/css" href="abc.css"></link>
  <style>h2{font-wight:bold;}</style>
  <script>
  $(document).ready(function(){
     $("#img").attr("src", "kkk.png");
  });
 </script>
 </head>
 <body>
    <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
    <script src="kkk.js" type="text/javascript"></script>
 </body>
</html>

执行流程大致如下:

  1. HTML文档已下载
  2. HTML文档的解析开始
  3. HTML解析范围 <script src="jquery.js" ...
  4. jquery.js 已下载并解析
  5. HTML解析范围 <script src="abc.js" ...
  6. abc.js 已下载,解析并运行
  7. HTML解析范围 <link href="abc.css" ...
  8. abc.css 已下载并解析
  9. HTML解析范围 <style>...</style>
  10. 内部CSS规则被解析和定义
  11. HTML解析范围 <script>...</script>
  12. 内部Javascript被解析并运行
  13. HTML解析范围 <img src="abc.jpg" ...
  14. abc.jpg 已下载并显示
  15. HTML解析范围 <script src="kkk.js" ...
  16. kkk.js 已下载,解析并运行
  17. HTML文档解析结束

请注意,由于浏览器的行为,下载可能是异步的并且是非阻塞的。例如,在Firefox中,此设置限制了每个域的同时请求数。

同样取决于组件是否已经被缓存,在不久的将来请求中可能不会再次请求该组件。如果组件已被缓存,则会从缓存而不是实际URL加载组件。

解析结束并且文档准备就绪并加载后,将onload触发事件。因此,当onload被触发时,将$("#img").attr("src","kkk.png");运行。所以:

  1. 文档准备就绪,触发了onload。
  2. JavaScript执行命中 $("#img").attr("src", "kkk.png");
  3. kkk.png 已下载并加载到 #img

$(document).ready()事件实际上是所有页面组件均已加载并准备就绪时触发的事件。进一步了解它:http : //docs.jquery.com/Tutorials : Introducing_$(document).ready()

编辑-此部分在平行部分或非平行部分详细说明:

默认情况下,根据我目前的理解,浏览器通常以3种方式运行每个页面:HTML解析器,Javascript / DOM和CSS。

HTML解析器负责解析和解释标记语言,因此必须能够调用其他两个组件。

例如,当解析器遇到此行时:

<a href="#" onclick="alert('test');return false;" style="font-weight:bold">a hypertext link</a>

解析器将进行3次调用,其中两次调用Javascript,一次调用CSS。首先,解析器将创建此元素并将其与所有与此元素相关的属性一起注册到DOM名称空间中。其次,解析器将调用将onclick事件绑定到此特定元素。最后,它将再次调用CSS线程,以将CSS样式应用于此特定元素。

执行是自上而下的,是单线程的。Javascript可能看起来是多线程的,但事实是Javascript是单线程的。这就是为什么在加载外部javascript文件时,HTML主页面的分析被暂停的原因。

但是,可以同时下载CSS文件,因为始终会应用CSS规则-也就是说,始终使用定义的最新CSS规则来重新粉刷元素-从而使其畅通无阻。

元素只有在解析后才能在DOM中使用。因此,当使用特定元素时,脚本始终放置在窗口onload事件之后或之内。

这样的脚本将导致错误(在jQuery上):

<script type="text/javascript">/* <![CDATA[ */
  alert($("#mydiv").html());
/* ]]> */</script>
<div id="mydiv">Hello World</div>

因为在解析脚本时,#mydiv元素仍未定义。而是可以这样:

<div id="mydiv">Hello World</div>
<script type="text/javascript">/* <![CDATA[ */
  alert($("#mydiv").html());
/* ]]> */</script>

要么

<script type="text/javascript">/* <![CDATA[ */
  $(window).ready(function(){
                    alert($("#mydiv").html());
                  });
/* ]]> */</script>
<div id="mydiv">Hello World</div>

4
谢谢。但是您提到下载可能由于浏览器的行为而异步并且是非阻塞的,那么可以在asyn中下载哪种组件(以FF为例)? <script>会阻止其他组件,对吗?对每个浏览器的规范都有参考吗?
朱涛

4
$(document).ready()在DOM完成时触发,而不是在所有页面组件都加载时触发
Pierre 2012年

2
@Pierre按页面组件的意思是DOM-> DOM中的任何组件。
毛里求斯2012年

3
只是为了澄清...常规的window.onload在#17之后发生...所以,jquery的$(document).ready()的代码在什么位置运行?#12?但是DOM本身在#1处加载对吗?
Armyofda12mnkeys

1
如果在<body>选项卡中,如果我们在<img>和<script>标记之间添加<link href =“ bootstrap.min.css” rel =“ stylesheet” / >而不是img,则在下载bootstrap之前不会显示img。所以我认为步骤[13],[14]需要修改...有人可以解释这种行为吗?
不丹2015年

34

1)HTML已下载。

2)HTML是逐步解析的。到达资产请求后,浏览器将尝试下载资产。大多数HTTP服务器和大多数浏览器的默认配置是仅并行处理两个请求。IE可以重新配置为并行下载无限数量的资产。Steve Souders已经能够在IE上并行下载100多个请求。例外是脚本请求会阻止IE中的并行资产请求。这就是为什么强烈建议将所有JavaScript放在外部JavaScript文件中,然后将请求放在HTML的结束body标签之前。

3)一旦解析了HTML,就渲染DOM。在几乎所有用户代理程序中,CSS都与DOM并行呈现。因此,强烈建议将所有CSS代码放入文档的<head> </ head>部分中要求尽可能高的外部CSS文件中。否则,将页面渲染到DOM中CSS请求位置的位置,然后从顶部开始渲染。

4)只有在DOM完全呈现并且页面中所有资产的请求都已解决或超时后,JavaScript才从onload事件执行。如果未从资产请求中收到HTTP响应,则IE7(我不确定IE8)不会很快使资产超时。这意味着JavaScript内联到页面的请求资产(即JavaScript写入HTML标记中而不包含在函数中)可能会阻止onload事件执行数小时。如果此类内联代码存在于页面中并且由于名称空间冲突导致代码崩溃而无法执行,则可以触发此问题。

在上述步骤中,最耗CPU的步骤是DOM / CSS的解析。如果要更快地处理页面,则可以通过消除多余的指令并将CSS指令整合到尽可能少的元素引用中来编写高效的CSS。减少DOM树中的节点数量也将加快渲染速度。

请记住,您从HTML甚至CSS / JavaScript资产请求的每个资产都需要单独的HTTP标头。这会消耗带宽,并且需要按请求进行处理。如果要使页面尽快加载,请减少HTTP请求的数量并减小HTML的大小。仅通过HTML就将页面权重平均为180k,并不会给用户带来任何好处。许多开发人员都承认一些谬论,即用户在6纳秒内就决定了页面内容的质量,然后从服务器清除DNS查询并在不满意的情况下烧毁了计算机,因此,他们提供了最漂亮的页面: HTML的25万。保持您的HTML简短美观,以便用户可以更快地加载您的页面。


2
将CSS指令整合到最少的元素Referrences中听起来很奇怪。如果需要设置三个元素的样式,则需要精确地引用三个元素。我不能将样式十引用一个,可以吗?或对此进行详细说明
绿色的

12

在Firefox中打开页面并获取HTTPFox插件。它会告诉您所有您需要的东西。

在archivist.incuito上找到此:

http://archivist.incutio.com/viewlist/css-discuss/76444

首次请求页面时,浏览器将GET请求发送到服务器,服务器将HTML返回给浏览器。然后,浏览器开始解析页面(可能在返回所有页面之前)。

当它找到对外部实体的引用时,例如CSS文件,图像文件,脚本文件,Flash文件或页面外部的任何其他内容(无论是否在同一服务器/域上),它都准备进行对该资源的进一步GET请求。

但是,HTTP标准指定浏览器对同一域的并发请求不应超过两个。因此,它将每个请求发送到队列中的特定域,并且在返回每个实体时,它将启动队列中该域的下一个请求。

返回实体所需的时间取决于其大小,服务器当前所承受的负载以及运行浏览器的计算机和服务器之间的每台计算机的活动。这些机器的清单原则上可以针对每个请求而有所不同,以至于一幅图像可能通过大西洋从美国传到我在英国的英国,而另一幅图像则是从同一服务器通过太平洋,亚洲和欧洲传来的,这需要更长的时间。因此,您可能会得到如下所示的序列,其中页面具有(按此顺序)对三个脚本文件和五个图像文件的引用,它们的大小均不同:

  1. GET script1和script2; 将script3和images1-5排队。
  2. script2到达(小于script1):GET script3,将图像1-5排队。
  3. script1到达;获取图像1,将图像2-5排队。
  4. image1到达,获取image2,将image3-5排队。
  5. 由于网络问题script3无法到达-再次获取script3(自动重试)。
  6. image2到达,script3仍然不在;获取图像3,将图像4-5排队。
  7. 图片3到达;GET image4,队列image5,script3仍在进行中。
  8. image4到达,获取image5;
  9. image5到达。
  10. script3到达。

简而言之:任何旧命令,取决于服务器在做什么,Internet其余部分在做什么,以及是否有任何错误并且必须重新获取。这似乎是一种怪异的处理方式,但是如果不这样做,Internet(不仅仅是WWW)就不可能以任何程度的可靠性工作。

另外,浏览器的内部队列可能无法按它们在页面中出现的顺序来获取实体-任何标准都不要求。

(哦,别忘了在浏览器和ISP用来减轻网络负载的缓存代理中缓存)。



2

AFAIK,浏览器(至少是Firefox)在解析每个资源后会立即请求。如果遇到img标签,它将在解析img标签后立即请求该图像。而且这甚至可以在它尚未接收到HTML文档的全部信息之前……也就是说,在这种情况下,它仍然可以下载HTML文档。

对于Firefox,有一些适用的浏览器队列,具体取决于about:config中的设置。例如,它将不会尝试从同一服务器一次下载超过8个文件……其他请求将排队。我认为在Mozilla网站上记录了每个域的限制,每个代理的限制以及其他内容,可以在about:config中进行设置。我读到某处IE没有这样的限制。

一旦下载了主要的HTML文档并对其进行了DOM解析,就会触发jQuery ready事件。然后,一旦下载并解析了所有链接的资源(CSS,图像等),就会触发load事件。jQuery文档中对此进行了明确说明。

如果您想控制所有内容的加载顺序,我相信最可靠的方法是通过JavaScript。



1

选择的答案似乎不适用于现代浏览器,至少在Firefox 52上不适用。我观察到的是,例如HTML解析器到达元素之前发出了诸如css,javascript之类的资源加载请求

<html>
  <head>
    <!-- prints the date before parsing and blocks HTMP parsering -->
    <script>
      console.log("start: " + (new Date()).toISOString());
      for(var i=0; i<1000000000; i++) {};
    </script>

    <script src="jquery.js" type="text/javascript"></script>
    <script src="abc.js" type="text/javascript"></script>
    <link rel="stylesheets" type="text/css" href="abc.css"></link>
    <style>h2{font-wight:bold;}</style>
    <script>
      $(document).ready(function(){
      $("#img").attr("src", "kkk.png");
     });
   </script>
 </head>
 <body>
   <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
   <script src="kkk.js" type="text/javascript"></script>
   </body>
</html>

我发现加载CSS和javascript资源的请求的开始时间没有被阻止。看起来Firefox具有HTML扫描功能,并且在开始解析HTML之前先确定关键资源(不包括img资源)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.