网页的加载和执行顺序?

IT技术 javascript html css
2021-01-30 19:15:56

我做过一些基于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加载完成后,浏览器开始并行加载结构中的外部资源并执行,直到所有资源都加载完毕。然后 DOM 会根据用户的行为根据 JS 进行更改。

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

谢谢。

编辑:

我用 Firebug 在 Firefox 中做了一个实验。它显示为下图: 替代文字

6个回答

根据你的样本,

<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 解析器负责解析和解释标记语言,因此必须能够调用其他 2 个组件。

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

<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>
$(document).ready() 在 DOM 完成时触发,而不是在所有页面组件加载时触发
2021-03-16 19:15:56
如果在 <body> 选项卡中,如果我们在 <img> 和 <script> 标签之间添加<link href="bootstrap.min.css" rel="stylesheet"/>,那么在下载 bootsrap 之前,不会显示 img ...所以我认为步骤 [13]、[14] 需要修改......有人可以解释这种行为吗?
2021-03-17 19:15:56
谢谢。但是你提到由于浏览器的行为,下载可能是异步非阻塞的,那么asyn可以下载什么样的组件(以FF为例)? <script>会阻塞其他组件,对吗?关于每个浏览器的规范的任何参考?
2021-03-31 19:15:56
@Pierre 通过页面组件我的意思是 DOM -> DOM 中的任何组件。
2021-04-01 19:15:56
只是为了澄清...常规 window.onload 发生在#17 之后...那么#jquery 的 $(document).ready() 代码在什么情况下运行?#12?但是 DOM 本身是在 #1 处加载的,对吗?
2021-04-04 19:15:56

1) 下载 HTML。

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

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

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

在上述步骤中,CPU 密集程度最高的步骤是解析 DOM/CSS。如果您希望您的页面处理得更快,那么通过消除冗余指令并将 CSS 指令合并到尽可能少的元素引用中来编写高效的 CSS。减少 DOM 树中的节点数量也会产生更快的渲染。

请记住,您从 HTML 甚至 CSS/JavaScript 资产请求的每个资产都使用单独的 HTTP 标头请求。这会消耗带宽并需要对每个请求进行处理。如果您想让页面加载速度尽可能快,请减少 HTTP 请求的数量并减小 HTML 的大小。仅从 HTML 中平均 180k 的页面权重并没有给您的用户体验带来任何好处。许多开发人员都认同一些谬论,即用户会在 6 纳秒内决定页面内容的质量,然后从他的服务器中清除 DNS 查询,如果不高兴则烧毁他的计算机,因此他们提供了最漂亮的页面250k 的 HTML。保持您的 HTML 简短而简洁,以便用户可以更快地加载您的页面。

将 CSS 指令整合到尽可能少的元素引用中听起来很奇怪。如果我需要设计三个元素的样式,我需要精确地引用三个元素。我不能参考样式十,可以吗?或者详细说明一下
2021-03-30 19:15:56

在 Firefox 中打开您的页面并获取 HTTPFox 插件。它会告诉你你需要的一切。

在 archivist.incuito 上找到了这个:

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

当您第一次请求一个页面时,您的浏览器会向服务器发送一个 GET 请求,服务器将 HTML 返回给浏览器。然后浏览器开始解析页面(可能在所有页面返回之前)。

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

然而,HTTP 标准规定浏览器不应向同一个域发出两个以上的并发请求。因此,它将对特定域的每个请求放入队列中,当每个实体返回时,它会启动该域队列中的下一个请求。

返回实体所需的时间取决于其大小、服务器当前正在经历的负载以及运行浏览器的机器和服务器之间的每台机器的活动。这些机器的列表原则上可以针对每个请求而不同,在某种程度上,一张图像可能从美国穿过大西洋传送到我在英国的我,而另一张来自同一服务器的图像则通过太平洋、亚洲和欧洲传出,这需要更长的时间。因此,您可能会得到如下序列,其中一个页面(按此顺序)引用了三个脚本文件和五个图像文件,所有文件的大小都不同:

  1. 获取 script1 和 script2;对脚本 3 和图像 1-5 的队列请求。
  2. 脚本 2 到达(它比脚本 1 小):获取脚本 3,将图像 1-5 排队。
  3. script1 到达;GET image1,排队images2-5。
  4. image1 到达,获取 image2,将 images3-5 排队。
  5. 由于网络问题,script3 无法到达 - 再次获取 script3(自动重试)。
  6. image2 到了,script3 还没到;GET image3,排队images4-5。
  7. 图3到了;GET image4, queue image5, script3 还在路上。
  8. image4 到达,获取 image5;
  9. image5 到了。
  10. 脚本3到了。

简而言之:任何旧订单,取决于服务器在做什么,互联网的其余部分在做什么,以及是否有任何错误并且必须重新获取。这似乎是一种奇怪的做事方式,但如果不这样做,互联网(不仅仅是 WWW)实际上不可能以任何程度的可靠性工作。

此外,浏览器的内部队列可能不会按照实体在页面中出现的顺序来获取实体——任何标准都没有要求。

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

如果您问这个问题是因为您想加快您的网站速度,请查看雅虎关于加快网站速度的最佳实践的页面它有很多加速网站的最佳实践。

AFAIK,浏览器(至少是 Firefox)在解析每个资源后立即请求它。如果它遇到 img 标签,它将在解析 img 标签后立即请求该图像。这甚至可以在它收到整个 HTML 文档之前......也就是说,在发生这种情况时它仍然可能正在下载 HTML 文档。

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

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

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