浏览器的多线程中,有的线程负责加载资源,有的线程负责执行脚本,有的线程负责渲染界面,有的线程负责轮询、监听用户事件。
这些线程,根据浏览器自身特点以及web标准等等,有的会被浏览器特意的阻塞。两个很明显的阻塞就是:脚本执行时对其他线程的阻塞和脚本加载时对其他线程的阻塞。
这两个阻塞发生在HTML页面初次解析时,它们对性能的影响较大,原因是:
document对象绑定了一个事件:DOMContentLoaded。这个事件会在DOM解析完成之后触发。这个事件触发之后(而不是window.load事件),会进入异步事件驱动阶段(另一个线程控制)。也就是说,DOM解析工作不完成,用户与页面的很多(并不是所有)事件交互就无法进行。这时候浏览器的忙指示(那个页面上方的烦人的旋转的圆圈)不会消失。
 
DOMContentLoaded什么时候触发?
DOMContentLoaded事件本身不会等待CSS文件、图片、iframe加载完成。
DOMContentLoaded的触发时机是:加载完页面,解析完所有标签(不包括执行CSS和JS),但是JS的执行,需要等待位于它前面的CSS加载(如果是外联的话)、执行完成,因为JS可能会依赖位于它前面的CSS计算出来的样式。所以:
  • 如果页面中没有script标签,DOMContentLoaded事件并没有等待CSS文件、图片加载完成。
  • 如果页面中静态的写有script标签,DOMContentLoaded事件需要等待JS执行完才触发。而且script标签中的JS需要等待位于其前面的CSS的加载完成。
注:现代浏览器会并发的预加载CSS、JS、IMG(例如:当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载)。但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)——先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。
 
 
所以就造成外部资源阻塞渲染,如CSS 与 JavaScript
  • 默认情况下,CSS 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。
  • JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。
默认情况下,CSS 被视为阻塞渲染的资源,存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。
总结如下:
  • css加载不会阻塞DOM树的解析
  • css加载会阻塞DOM树的渲染
  • css加载会阻塞后面js语句的执行
css会阻塞js,同理,css也会阻塞img解码、paint(浏览器认为你的CSS没有加载完毕,不确定图片的样式到底如何,牵扯到重绘资源问题),js不会阻塞img的解码、paint(估计chrome做了优化,具体本人还不知,希望客官补充)。
css阻塞优化:
  • 还可以用媒体类型(media type)和媒体查询(media query)来解除对渲染的阻塞。 media=“print",会加载,但不会阻塞;media="(min-width:320px)",会在符合查询条件下阻塞(适配css会执行)
  • 大css文件拆分成多个小css文件,并发加载
因为渲染线程和js线程与资源进行加载的线程并不互斥,不会互斥意味着:资源的加载可以和UI渲染、重排,事件响应,或者JavaScript代码的执行的并发进行。
所以资源加载器线程会一直进行并发加载。
这里还有一个知识点:下载的最大并行数指的是从一个主机上下载的最大并行数,如果从多个主机下载资源,这个数量会翻倍,但是由于对DNS的解析也是一个性能优化的点,故而一般策略是:不应设置超过4个主机,最好只设置2个主机。
但是操蛋的就是,如果浏览器解析DOM时需要下载脚本资源,那么下载这个资源的线程就是阻塞其他下载线程以及渲染线程,导致渲染速度变慢。
但是假设该脚本下载的速度较慢,而且多个脚本非并发下载,并且假如多个<script>内脚本执行时间较长的话,DOM解析工作还是会一直完不成。
故而我们需要无阻塞加载脚本的技术。
 
js阻塞优化
因为:脚本执行和渲染DOM的并发可能会引发严重的冲突(脚本可以修改DOM)
所以:JavaScript引擎和渲染引擎所在的两个线程被设计为互斥的!
这就意味着:在执行<script>中内容时,浏览器会切换到JavaScript引擎所在的线程,此时渲染引擎所在的线程会阻塞,故其后元素的解析和渲染会暂停。这时候如果脚本执行时间太长的话,不仅后面的元素会一直看不到,对DOM的解析工作也会一直完不成。用户会陷入焦急的等待中。
为了防止javascript阻塞,我们会
1、把<script>放到紧跟</body>之前的位置
这样就不会影响需要放到页面上的UI元素的解析了。这样的好处就是,用户能即使看到页面上的UI元素,而防止出现了浏览器白屏等现象。
2、动态脚本元素-不重要的js动态插入。
因为document.createElement("script")的async属性默认为true,而document.head.appendChild代码之后,由于没有触发渲染树的重绘,切换回的渲染线程会将剩下的DOM解析并渲染完毕。同时新插入的<script>中的资源也会并发的下载。
var script=document.createElement("script"); console.log(script.async);//true
同理:用XHR对象下载代码,并注入到页面也可以达到同样的效果
如果需要同步执行,需要将async属性设置为fasle
3、h5时代,script添加defer或asyn两个属性(html4.0中定义了defer;html5.0中定义了async)
 
  • 如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。即:整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。defer不会改变script中代码执行顺序
  • 如果 script 标签中包含 async,则 HTML 文档构建不受影响,不需要等待 async-script 执行。但是,async-script 加载完成后,就会立即执行!如果页面还是没有解析完成,就会停下来(阻塞页面)等此脚本执行完毕再继续解析。async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。而且:多个 async-script 的执行顺序是不确定的。
 
 
document.readyState
 
说道DOMContentLoaded,不得不提readystatechange,通过document.readyState值来更进一步来判断文档状态:
  1. uninitiated:xml 对象被产生,但没有任何文件被加载。
  2. loading:document正在下载,文件尚未开始解析。
  3. loaded:部分的文件已经加载且进行解析,但对象模型尚未生效。
  4. interactive:document完成了解析,但是资源还在下载,对象模型是有效但只读的。
  5. complete:代表加载成功,文档加载完成,并且所有resource都加载完毕
通过下面代码验证,在chrome上貌似只有 interactive和complete。
document.addEventListener("DOMContentLoaded",function () { console.log("DOMContentLoaded"+new Date()) }); document.addEventListener("readystatechange",function () { console.log("B_____"+new Date()); console.log(document.readyState) // switch (document.readyState){ // case "loading": // console.log("LOADING"+new Date()); // break; // case "loaded": // console.log("loaded"+new Date()); // break; // case "interactive": // console.log("interactive"+new Date()); // break; // case "complete": // console.log("complete"+new Date()); // break; // } }); console.time("A")
 
A: 5.89208984375ms
B_____Thu May 17 2018 10:23:36 GMT+0800 (CST)
interactive
DOMContentLoadedThu May 17 2018 10:23:36 GMT+0800 (CST)
B_____Thu May 17 2018 10:23:36 GMT+0800 (CST)
complete
但是,今天看了:你不知道的 DOMContentLoaded
这里又有疑问:interactive DOMContentLoaded complete onload三个先后顺序是什么呢?
DOMContentLoaded和interactive:表示文档解析完成,且资源未完全加载完成。区别呢?执行顺序呢?
验证表明:interactive 》DOMContentLoaded 》 complete 》 onload
但是,DOMContentLoaded触发时候,document.readyState一般是interactive,也有可能complete。而当页面有大量的二进制文件(页面加载的时长大于阻塞的时长的时候),document.readyState=complete 可能反而在 onload 事件之后才能触发(这个我未完成验证出这种情况)
我觉得onreadystatechange这个不是很靠谱,一般用DOMContentLoaded判断页面解析完全。希望哪位大牛提供这方面的补充,感激不尽!
在图片上,也有onload跟complete
document.getElementById('load').onclick = function() { var img = new Image(); if(img.complete) { console.log('dd'); } img.onload = function() { console.log('ff') } img.src="data:images/1-logo.png"; }
这里顺带提下img加载相关 属性
  • onload:表示加载好,换言之,没有加载好不会执行;
  • onAbort:图片加载的时候,用户通过点击停止加载时出发
  • onerror:如果图片不存在(网络很不通畅,也可能触发 onerror事件)
  • complete:图片显示出来以后为true,
文有不妥之处,请告知,谢谢!
参考文章:
 

再谈DOMContentLoaded与渲染阻塞—分析html页面事件与资源加载的更多相关文章

  1. 细谈unity资源加载和卸载

    转载请标明出处:http://www.cnblogs.com/zblade/ 一.概要 在了解unity的资源管理方式之后,接下来细谈一下Unity的资源是如何从磁盘中加载到运行时的内存中,以及又是如 ...

  2. 【原】从一个bug浅谈YUI3组件的资源加载

    篇前声明:为了不涉及业务细节,篇内信息统一以某游戏,某功能代替 前不久,某游戏准备内测客户端,开发人员测试过程中发现某功能突然不灵了,之前的测试一切ok,没有发现任何异常,第一反应是,游戏内浏览器都是 ...

  3. Spring源码分析(十一)bean的加载

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 经过前面的分析,我们终于结束了对XML配置文件的解析,接下来将会面临更大 ...

  4. NET Core微服务之路:再谈分布式系统中一致性问题分析

    前言 一致性:很多时候表现在IT系统中,通常在分布式系统中,必须(或最终)为多个节点的数据保持一致.世间万物,也有存在相同的特征或相似,比如儿时的双胞胎,一批工厂流水线的产品,当然,我们不去讨论非IT ...

  5. React Native 断点调试 跨域资源加载出错问题的原因分析

    写在前面 ————如果从头开始看还没解决,试试文章最后的绝招 闲来无事,折腾了一下React Native,相比之前,开发体验好了不少.但在真机断点调试那里遇到了跨域资源加载出错的问题,一番探索总算解 ...

  6. 高性能javascript 文件加载阻塞

    高性能javascript   javascript脚本执行过程中会中断页面加载,直到脚本执行完毕,此操作阻塞了页面加载,造成性能问题. 脚本位置和加载顺序:如果将脚本放在head内,那么再脚本执行完 ...

  7. 再谈 webpack build 及 加载优化

    之前项目多,事情忙,一直没时间写博客,现在空闲下来了,总结一下 之前讲过了关于 build 压缩文件的方法,有兴趣的可以看下: 点击查看 现在讲讲一个页面的首屏加载速度该如何提升 提前说明 需要 we ...

  8. 浏览器加载和渲染HTML的过程(标准定义的过程以及现代浏览器的优化)

    先看一下标准定义的浏览器渲染过程(网上找的): 浏览器打开网页的过程 用户第一次访问网址,浏览器向服务器发出请求,服务器返回html文件: 浏览器开始载入html代码,发现 head 标签内有一个 l ...

  9. jQuery实现DOM加载方法源码分析

    传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... }  但 ...

随机推荐

  1. 基于opencv的gpu与cpu对比程序,代码来自opencv的文档中

    原文链接: http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/gpu/gpu-basics-similarity/gpu-basi ...

  2. The 15th tip of DB Query Analyzer

    The 15th tip of DB Query Analyzer      ---- SQL Execute Schedule function is realized in 6.01 Ma Gen ...

  3. mac os x下的一些小技巧

    1显示swap空间: sysctl vm.swapusage 其中sysctl中有很多可以控制和查看的项,可以通过sysctl -A列举,另外可以通过man sysctl来查看. 而实际swap文件和 ...

  4. listview中的adapter学习小结

    概述 Adapter是数据和UI之间的一个桥梁,在listview,gridview等控件中都会使用到,android给我们提拱了4个adapte供我们使用: BaseAdapter是一个抽象类,继承 ...

  5. 使用vs2010 opencv2.4.4编译release版本程序

    大体上要注意一下几点内容: 1.vc++目录的选择上,库目录选择为opencv目录中的staticlib目录 2.在链接->输入->附加依赖库,中添加,相应的staticlib库目录中的所 ...

  6. javascript中的instanceof运算符

    instanceof运算符希望左操作数是一个对象,右操作数表示对象的类:如果左侧的对象是右侧类的实例,则返回true,否则返回false.由于js中对象的类是通过初始化它们的构造函数来定义的,因此in ...

  7. 2018 .NET开发者调查报告: .NET Core 是怎么样的状态

    4月28日,在公众号里发起<.NET Core 使用调查>,该调查为期一周,有近3300名开发者参与. 已经使用.net core  的人数只有44%,计划使用.net core 比例达到 ...

  8. Integer 和int 比较

    在jdk1.5的环境下,有如下4条语句: 1 2 3 4 Integer i01 = 59; int i02 = 59; Integer i03 =Integer.valueOf(59); Integ ...

  9. javascript学习(三)——常用方法(2)

    一.兼容性较高的浏览器页面关闭 //关闭网页,不支持火狐(火狐返回上次浏览页面)   //FireFox非window.open()等弹出页面,需要在地址栏中输入about:config, 然后将do ...

  10. 新型USB病毒BadUSB 即使U盘被格式化也无法根除

    这种病毒并不存在于USB设备中的存储文件中,而是根植于USB设备的固件里.这意味着,即使用户对U盘进行全面的格式化清理,仍不能"杀死"它.