JavaScript无阻塞加载和defer、async详解


Posted in Javascript onFebruary 26, 2017

无阻塞加载

把js放在head里,浏览器是怎么去执行它的呢,是按顺序加载还是并行加载呢?在旧的浏览器下,都是按照先后顺序来加载的,这就保证了加载的js依赖不会发生问题。但是少部分新的浏览器已经开始允许并行加载js了,也就是说可以同时下载js文件,但是还是按先后顺序执行文件的。

下载是异步的没问题,但是每个javascript执行的时候还是同步的,就是先出现的script标签一定是先执行,即使是并行下载它是最后一个下载完成的,除非标有defer的script标签。任何javascript在执行的时候都会中断当前html文档解析,自然会阻止页面渲染。

javascript加载是不会影响已经渲染的页面,但是会中断html文档解析,浏览器会在javascript执行以后决定当前文档是否需要进行重新渲染或者文档重排。所以即使javascript放到最后面也会使浏览器暂停,但不影响之前已经解析出来的dom文档,此时对于用户来说是可操作的。

javascript下载完毕之后会立即执行,所有的javascript执行都会阻塞浏览器的其他行为,例如阻塞其他javascript的执行、其他的http请求的执行以及页面的解析和渲染。(html文档中外部js的下载也会阻塞浏览器的行为,但通过创建script元素动态js的下载不会,可能是认为动态的js不会改变页面效果,所以允许资源并行下载。)

JavaScript无阻塞加载和defer、async详解

图示动态脚本的下载

UI线程会根据页面里资源(资源是指css文件,图片等等)书写的先后顺序来加载资源,加载资源也就是使用http请求获取资源,像css外部文件,html文件以及图片等资源http请求处理完毕也就意味着资源加载结束,但是像外部的javascript文件的加载则不同,它的加载过程被分为两步,第一步和加载css文件和图片一样,就是执行一个http请求下载外部的js文件,但是javascript完成http操作后并不意味操作完毕,UI线程接着会执行它。js脚本的下载和执行必须是一个完整的操作,是不能被割裂的。动态js的下载不会阻塞,但执行一定会会。

浏览器为了提升用户体验,加快UI线程的执行是一个无法回避的问题,但是拆分js的下载和执行是不可行的,如是乎浏览器换了种方式,这个方式也就是在同一个时间能下载多个资源。

将常用的,稳定的静态资源统一放在一个静态资源服务器上,由统一的域名对外提供,这个域名要和主体请求的域名不一样,原理是因为浏览器只通过域名来限制连接的个数,如果一个页面里有两个不同的域的,那么并行的http请求个数也会变成两倍。有度,对DNS解析要开销,所以2个最佳。

将所有外部js代码分为UI初始化代码和其他代码,UI初始化代码是在页面加载时候执行的代码。让那些不会用于页面初始化展示的js代码的加载和执行操作通过onload事件在浏览器忙指示结束后触发,即让那些和页面加载无关的js脚本在onload方法里执行

无阻塞加载脚本的核心技术就是动态的创建script的dom节点,而且可以跨域访问。

var script=document.createElement("script");

script.type="text/javascript";

script.src="file.js";

document.getElementsByTagName("head")[0].appendChild(script);

动态脚本元素,就是说 <script> 标签不是写死在HTML中的,而是由现有的脚本生成的,因为 <script> 标签也是DOM元素的一种,而JavaScript是可以通过DOM API操作DOM的。动态脚本只有在新建的script元素被添加到html文档时开始下载,下载完立即执行。

无阻塞脚本的好处就是不会阻塞UI的执行,也不会影响其他同步js代码的执行,不无阻塞脚本改变了脚本的加载顺序,所以在使用无阻塞脚本时候一定要更加注意脚本之间的依赖关系,保证整个页面的脚本都能正常执行。

使用无阻塞脚本了,代码置于head标签还是html文档底部也就无关紧要了。

页面加载的总时间不是衡量页面加载快捷的标准,页面同步阻塞加载的时间才是衡量页面加载效率的准确标准,非阻塞脚本加载可能会增加整个页面加载的时间,但是它可以减少页面阻塞加载的时间。

脚本的异步执行,会产生前后依赖的问题。在脚本加载执行完毕后,非ie浏览器会触发该 <script> 元素的 onload 事件,ie浏览器下有onreadystatechange事件,我们可以将回调放到这个事件中处理。

每当浏览器解析到<script>标签(无论内嵌还是外链)时,浏览器会优先下载、解析并执行该标签中的javaScript代码,而阻塞其后所有页面内容的下载和渲染。(也就是说外部js的下载也会阻塞别的线程,目前有少部分浏览器支持并行下载js)

无阻塞加载脚本技术的核心就是:动态下载js脚本的时候,不会阻塞UI线程的执行。动态脚本为什么不阻塞ui线程?可能是因为浏览器认为动态资源不会影响页面渲染。

让script延迟和异步的两个属性:defer和async

js脚本会改变文档输入流的内容,所以执行js时会暂停页面的渲染。对于内联脚本没什么问题,因为脚本和html文档被同时加载了。但对于外部引入的脚本,脚本的下载(取决于网速)也会阻塞浏览器文档的解析和渲染,甚至会阻塞有些浏览器下载别的资源(目前有些浏览器已经实现并行下载)。所以出现defer和async属性,优化页面的显示。

defer(延迟)是html4.0中定义的,该属性使得浏览器能延迟脚本的下载,等document文档载入和解析完成后,按照他们在文档中出现顺序再去下载解析。也就是说defer属性的<script>就类似于将<script>放在body底部的效果,会在document的DOMContentLoaded事件之前执行。

将脚本放在body底部比给脚本增加defer属性让脚本延迟加载更好。

async(异步)是HTML5新增的属性,该属性的作用是让浏览器能并行下载脚本且不阻塞浏览器的文档解析和渲染,下载完成后脚本立即执行,可能无序执行,取决于下载完成的时间)

若浏览器同时支持上述两种属性且script标签同时具有这两种属性,则async属性会优于defer生效。

在不支持async属性的浏览器里,可以通过动态创建script元素并插入文档中,实现脚本的异步载入和执行:

JavaScript无阻塞加载和defer、async详解

requirejs就是使用这个方法实现的。

Javascript 相关文章推荐
JavaScript 动态添加表格行 使用模板、标记
Oct 24 Javascript
六款帮助你实现惊艳视差滚动效果的jQuery插件
Sep 14 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
Apr 30 Javascript
详解JS面向对象编程
Jan 24 Javascript
JS中call/apply、arguments、undefined/null方法详解
Feb 15 Javascript
对象转换为原始值的实现方法
Jun 06 Javascript
Vue 兄弟组件通信的方法(不使用Vuex)
Oct 26 Javascript
基于vue中解决v-for使用报红并出现警告的问题
Mar 03 Javascript
基于webpack.config.js 参数详解
Mar 20 Javascript
react PropTypes校验传递的值操作示例
Apr 28 Javascript
vue路由分文件拆分管理详解
Aug 13 Javascript
vue-cli中实现响应式布局的方法
Mar 02 Vue.js
浅谈JavaScript中的apply/call/bind和this的使用
Feb 26 #Javascript
JavaScript中Promise的使用详解
Feb 26 #Javascript
setTimeout函数的神奇使用
Feb 26 #Javascript
node.js入门学习之url模块
Feb 25 #Javascript
从零学习node.js之利用express搭建简易论坛(七)
Feb 25 #Javascript
从零学习node.js之express入门(六)
Feb 25 #Javascript
Node.JS中事件轮询(Event Loop)的解析
Feb 25 #Javascript
You might like
解析smarty模板中类似for的功能实现
2013/06/18 PHP
PHP加密函数 Javascript/Js 解密函数
2013/09/23 PHP
PHP判断表单复选框选中状态完整例子
2014/06/24 PHP
[原创]php逐行读取txt文件写入数组的方法
2015/07/02 PHP
PHP实现的贪婪算法实例
2017/10/17 PHP
Nigma vs AM BO3 第一场2.13
2021/03/10 DOTA
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
2007/02/09 Javascript
Jsonp 跨域的原理以及Jquery的解决方案
2011/06/27 Javascript
javascript检查浏览器是否已经启用XX功能
2015/07/10 Javascript
jQuery插件boxScroll实现图片轮播特效
2015/07/14 Javascript
使用jQuery获取data-的自定义属性
2015/11/10 Javascript
BootStrap下拉框在firefox浏览器界面不友好的解决方案
2016/08/18 Javascript
jQuery Validate插件ajax方式验证输入值的实例
2017/12/21 jQuery
详解Angular6 热加载配置方案
2018/08/18 Javascript
laydate时间日历插件使用方法详解
2018/11/14 Javascript
微信小程序版本自动更新的方法
2019/06/14 Javascript
[01:22:42]2014 DOTA2华西杯精英邀请赛 5 24 DK VS LGD
2014/05/26 DOTA
[49:27]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第一场
2018/04/05 DOTA
python下函数参数的传递(参数带星号的说明)
2010/09/19 Python
浅析python协程相关概念
2018/01/20 Python
python爬虫爬取监控教务系统的思路详解
2020/01/08 Python
Python Opencv中用compareHist函数进行直方图比较对比图片
2020/04/07 Python
VSCode中autopep8无法运行问题解决方案(提示Error: Command failed,usage)
2021/03/02 Python
使用CSS禁止textarea调整大小功能的方法
2015/03/13 HTML / CSS
英国最大的电脑零售连锁店集团:PC World
2016/10/10 全球购物
锐步香港官方网上商店:Reebok香港
2020/11/05 全球购物
电大自我鉴定
2013/10/27 职场文书
公司司机岗位职责
2014/02/07 职场文书
见习报告格式要求
2014/11/04 职场文书
2014年卫生院工作总结
2014/12/03 职场文书
2015年毕业实习工作总结
2015/05/29 职场文书
邹越演讲观后感
2015/06/15 职场文书
2016年七夕情人节宣传语
2015/11/25 职场文书
小学一年级数学教学反思
2016/02/16 职场文书
详解MySQL数据库千万级数据查询和存储
2021/05/18 MySQL
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers