网页加载历程详解

网页加载历程详解

网页加载过程详解

  网页加载的详细步骤拆分:

  1、用户打开url链接

  2、浏览器查询url的dns地址

  3、提交url请求到服务器端

  4、服务器端处理

  5、传输处理好的html文本内容到浏览器

  6、浏览器解析html,并加载css,js,图片等内容

  7、加载完成,用户看到完整的页面内容

  其中第六步的浏览器解析过程将是前端优化最直接、有效的地方,该过程的详细过程为:接受网络数据-》将二进制码变成字符-》将字符变为unicode code points.-》tokenizer -》tree constructor -》DOM ready -》Window ready。前端编程,主要理解的是tree constructor -> dom ready -> window ready的流程。

  浏览器加载和渲染html的顺序

  1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。

  2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)。

  3. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。

  4. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。

  5. JS、CSS中如有重定义,后定义函数将覆盖前定义函数。

  JS的加载(js是单线下载和执行的)

  1. 不能并行下载和解析(阻塞下载)。

  2. 当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有

  代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修

  改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现.

  如何加快HTML页面加载速度

  1. 页面减肥:

  a. 页面的肥瘦是影响加载速度最重要的因素。

  b. 删除不必要的空格、注释。

  c. 将inline的script和css移到外部文件。

  d. 可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥。

  2. 减少文件数量:

  a. 减少页面上引用的文件数量可以减少HTTP连接数。

  b. 许多JavaScript、CSS文件可以合并最好合并,人家财帮子都把自己的JavaScript. functions和Prototype.js合并到一个base.js文件里去了。

  3. 减少域名查询:

  a. DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好。

  4. 缓存重用数据:

  a. 对重复使用的数据进行缓存。

  5. 优化页面元素加载顺序:

  a. 首先加载页面最初显示的内容和与之相关的JavaScript和CSS,然后加载HTML相关的东西,像什么不是最初显示相关的图片、flash、视频

  等很肥的资源就最后加载。

  6. 减少inline JavaScript的数量:

  a. 浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大。

  b. 不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容。

  7. 使用现代CSS和合法的标签:

  a. 使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片。

  b. 使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥。

  8. Chunk your content:

  a. 不要使用嵌套table,而使用非嵌套table或者div。将基于大块嵌套的table的layout分解成多个小table,这样就不需要等到整个页面(或大table)内容全部加载完才显示。

  9. 指定图像和table的大小:

  a. 如果浏览器可以立即决定图像或table的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作。

  b. 这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。

  本文总结一下浏览器在 javascript 的加载方式。

  关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy execution),async 属性, defer 属性

  一、同步加载与异步加载的形式

  1. 同步加载

  我们平时最常使用的就是这种同步加载形式:

  <script src=""$amp;>amp;$lt;/script>

  同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。

  js 之所以要同步执行,是因为 js 中可能有输出 document 内容、修改dom、重定向等行为,所以默认同步执行才是安全的。

  以前的一般建议是把<script>放在页面末尾</body>之前,这样尽可能减少这种阻塞行为,而先让页面展示出来。

  简单说:加载的网络 timeline 是瀑布模型,而异步加载的 timeline 是并发模型。

2. 常见异步加载(Script DOM Element)

  (function() { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = ''; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })();


异步加载又叫非阻塞,浏览器在下载执行 js 同时,还会继续进行后续页面的处理。

  这种方法是在页面中<script>标签内,用 js 创建一个 script 元素并插入到 document 中。这样就做到了非阻塞的下载 js 代码。

  async属性是HTML5中新增的异步支持,见后文解释,加上好(不加也不影响)。

  此方法被称为 Script DOM Element 法,不要求 js 同源。

  将js代码包裹在匿名函数中并立即执行的方式是为了保护变量名泄露到外部可见,这是很常见的方式,尤其是在 js 库中被普遍使用。

  例如 Google Analytics 和 Google+ Badge 都使用了这种异步加载代码:

  (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();

  (function()

  {var po = document.createElement("script"); po.type = "text/javascript"; po.async = true;po.src = ""; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(po, s); })();

  但是,这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理。

3. onload 时的异步加载

  (function() { function async_load(){ var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = ''; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); } if (window.attachEvent) window.attachEvent('onload', async_load); else window.addEventListener('load', async_load, false); })();

  这和前面的方式差不多,但关键是它不是立即开始异步加载 js ,而是在 onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。

  补充:DOMContentLoaded 与 OnLoad 事件

  DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。但是页面中引用的图片、subframe可能还没有加载完。

  OnLoad:页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。

  这两个时间点将页面加载的timeline分成了三个阶段。

4.异步加载的其它方法

  由于Javascript的动态特性,还有很多异步加载方法:

  

  • XHR Eval
  • XHR Injection
  • Script in Iframe
  • Script Defer
  • document.write Script Tag
  • 还有一种方法是用 setTimeout 延迟0秒 与 其它方法组合。

  XHR Eval :通过 ajax 获取js的内容,然后 eval 执行。

  var xhrObj = getXHRObject(); xhrObj.onreadystatechange = function() { if ( xhrObj.readyState != 4 ) return; eval(xhrObj.responseText); }; xhrObj.open('GET', 'A.js', true); xhrObj.send('');

  Script in Iframe:创建并插入一个iframe元素,让其异步执行 js 。

  var iframe = document.createElement('iframe'); document.body.appendChild(iframe); var doc = iframe.contentWindow.document; doc.open().write('<body οnlοad="insertJS()"$amp;>apos;$); doc.close();

  GMail Mobile:页内 js 的内容被注释,所以不会执行,然后在需要的时候,获取script元素中 text 内容,去掉注释后 eval 执行。

  <script type="text/javascript"> /* var ... */ </script>

  详见参考资料中2010年的Velocity 大会 Steve Souders 和淘宝的那两个讲义。

  二、async 和 defer 属性

  1. defer 属性

  <script src="file.js" defer$amp;>amp;$lt;/script>

  defer属性声明这个脚本中将不会有 document.write 或 dom 修改。

  浏览器将会并行下载 file.js 和其它有 defer 属性的script,而不会阻塞页面后续处理。

  defer属性在IE 4.0中就实现了,超过13年了!Firefox 从 3.5 开始支持defer属性 。

  注:所有的defer 脚本保证是按顺序依次执行的。

  2. async 属性

  <script src="file.js" async$amp;>amp;$lt;/script>

  async属性是HTML5新增的。作用和defer类似,但是它将在下载后尽快执行,不能保证脚本会按顺序执行。它们将在onload 事件之前完成。

  Firefox 3.6、Opera 10.5、IE 9 和 最新的Chrome 和 Safari 都支持 async 属性。可以同时使用 async 和 defer,这样IE 4之后的所有 IE 都支持异步加载。

  3. 详细解释

  <script> 标签在 HTML 4.01 与 HTML5 的区别:

  

  • type 属性在HTML 4中是必须的,在HTML5中是可选的。
  • async 属性是HTML5中新增的。
  • 个别属性(xml:space)在HTML5中不支持。

  说明:

  

  1. 没有 async 属性,script 将立即获取(下载)并执行,然后才继续后面的处理,这期间阻塞了浏览器的后续处理。
  2. 如果有 async 属性,那么 script 将被异步下载并执行,同时浏览器继续后续的处理。
  3. HTML4中就有了defer属性,它提示浏览器这个 script 不会产生任何文档元素(没有document.write),因此浏览器会继续后续处理和渲染。
  4. 如果没有 async 属性 但是有 defer 属性,那么script 将在页面parse之后执行。
  5. 如果同时设置了二者,那么 defer 属性主要是为了让不支持 async 属性的老浏览器按照原来的 defer 方式处理,而不是同步方式。

  另参见官方说明:script async

  个人补充:

  既然 HTML5 中已经支持异步加载,为什么还要使用前面推荐的那种麻烦(动态创建 script 元素)的方式?

  答:为了兼容尚不支持 async 老浏览器。如果将来所有浏览器都支持了,那么直接在script中加上async 属性是最简单的方式。

  三、延迟加载(lazy loading)

  前面解决了异步加载(async loading)问题,再谈谈什么是延迟加载。

  延迟加载:有些 js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。延迟加载就是一开始并不加载这些暂时不用的js,而是在需要的时候或稍后再通过js 的控制来异步加载。

  也就是将 js 切分成许多模块,页面初始化时只加载需要立即执行的 js ,然后其它 js 的加载延迟到第一次需要用到的时候再加载。

  特别是页面有大量不同的模块组成,很多可能暂时不用或根本就没用到。

  就像图片的延迟加载,在图片出现在可视区域内时(在滚动条下拉)才加载显示图片。

  四、script 的两阶段加载 与 延迟执行(lazy execution)

  JS的加载其实是由两阶段组成:下载内容(download bytes)和执行(parse and execute)。

  浏览器在下载完 js 的内容后就会立即对其解析和执行,不管是同步加载还是异步加载。

  前面说的异步加载,解决的只是下载阶段的问题,但代码在下载后会立即执行。

  而浏览器在解析执行 JS 阶段是阻塞任何操作的,这时的浏览器处于无响应状态。

  我 们都知道通过网络下载 script 需要明显的时间,但容易忽略了第二阶段,解析和执行也是需要时间的。script的解析和执行所花的时间比我们想象的要多,尤其是script 很多很大的时候。有些是需要立刻执行,而有些则不需要(比如只是在展示某个界面或执行某个操作时才需要)。

  这些script 可以延迟执行,先异步下载缓存起来,但不立即执行,而是在第一次需要的时候执行一次。

  利用特殊的技巧可以做到 下载 与 执行的分离 (再次感谢 javascript 的动态特性)。比如将 JS 的内容作为 Image或 object 对象加载缓存起来,所以就不会立即执行了,然后在第一次需要的时候再执行。

  此部分的更多解释 请查看末尾参考资料中 ControlJS 的相关链接。

  小技巧:

  1. 模拟较长的下载时间

  写个后端脚本,让其 sleep 一定时间。如在 jsp 中 Thread.sleep(5000); ,这样5秒后才能收到内容。

  2. 模拟较长的 js 代码执行时间(因为这步一般比较快不容易观察到):

  var t_start = Number(new Date());

  while ( t_start + 5000 > Number(new Date()) ) {}

  这个代码将使 js 执行5秒才能完成!

  五、script 标签使用的历史

  1. script 放在 HEAD 中

  <head> <script src=“…”$amp;>amp;$lt;/script> </head>

  • 阻止了后续的下载;
  • 在IE 6-7 中 script 是顺序下载的,而不是现在的 “并行下载、顺序执行” 的方式;
  • 在下载解析执行阶段阻止渲染(rendering);

  2. script 放在页面底部(2007)

  ... <script src=“…”$amp;>amp;$lt;/script> </body>

  • 不阻止其它下载;
  • 在IE 6-7 中 script 是顺序下载的;
  • 在下载解析执行阶段阻止渲染(rendering);

  3. 异步加载script(2009)

  var se = document.createElement ('script'); se.src = ''; document.getElementsByTagName('head') [0].appendChild(se);

  这就是本文主要说的方式。

  

  • 不阻止其它下载;
  • 在所有浏览器中,script都是并行下载;
  • 只在解析执行阶段阻止渲染(rendering);

  4. 异步下载 + 按需执行 (2010)

  var se = new Image(); se.onload = registerScript(); se.src = ''; 把下载 js 与 解析执行 js 分离出来

  

  • 不阻止其它下载;
  • 在所有浏览器中,script都是并行下载;
  • 不阻止渲染(rendering)直到真正需要时;

  六、异步加载的问题

  在异步加载的时候,无法使用 document.write 输出文档内容。

  在同步模式下,document.write 是在当前 script 所在的位置输 出文档的。而在异步模式下,浏览器继续处理后续页面内容,根本无法确定 document.write 应该输出到什么位置,所以异步模式下 document.write 不可行。而到了页面已经 onload 之后,再执行 document.write 将导致当前页面的内容被清空,因为它会自动触发 document.open 方法。

  实际上document.write的名声并不好,最好少用。

  替代方法:

  1. 虽然异步加载不能用 document.write,但还是可以onload之后执行操作dom(创建dom或修改dom)的,这样可以实现一些自己的动态输出。比如要在页面异步创建一个浮动元素,这和它在页面中的位置就没关系了,只要创建出该dom元素添加到 document 中即可。

  2. 如果需要在固定位置异步生成元素的内容,那么可以在该固定位置设置一个dom元素作为目标,这样就知道位置了,异步加载之后就可以对这个元素进行修改。

  六、JS 模块化管理

  异步加载,需要将所有 js 内容按模块化的方式来切分组织,其中就存在依赖关系,而异步加载不保证执行顺序。

  另外,namespace 如何管理 等相关问题。这部分已超出本文内容,可参考:

  RequireJS 、 CommonJS 以及 王保平(淘宝)的 SeaJS 及其博客 。

  七、JS最佳实践:

  1. 最小化 js 文件,利用压缩工具将其最小化,同时开启尽量不要放在 <head> 中,尽量放在页面底部,最好是</body>之前的位置

  3. 避免使用 document.write 方法

  4. 异步加载 js ,使用非阻塞方式,就是此文内容。

  5. 尽量不直接在页面元素上使用 Inline Javascript,如onClick 。有利于统一维护和缓存处理。

  参考资料:

  Lazy Loading Asyncronous Javascript

  Load Non-blocking JavaScript with HTML5 Async and Defer

  2010年 Velocity China 上的两个讲义:

  Steve Souders(Google)的 Even Faster Web Sites (pdf)

  李穆(淘宝)的 第三方广告代码稳定性和性能优化实战 (pdf)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值