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 相关文章推荐
js与jquery实时监听输入框值的oninput与onpropertychange方法
Feb 05 Javascript
jQuery实现右下角可缩放大小的层完整实例
Jun 20 Javascript
JS 循环li添加点击事件 (闭包的应用)
Dec 10 Javascript
JS字符串按逗号和回车分隔的方法
Apr 25 Javascript
详解ElementUI之表单验证、数据绑定、路由跳转
Jun 21 Javascript
jQuery图片缩放插件smartZoom使用实例详解
Aug 25 jQuery
图文讲解用vue-cli脚手架创建vue项目步骤
Feb 12 Javascript
详解关于element级联选择器数据回显问题
Feb 20 Javascript
ElementUI Tag组件实现多标签生成的方法示例
Jul 08 Javascript
Elementui表格组件+sortablejs实现行拖拽排序的示例代码
Aug 28 Javascript
layui button 按钮弹出提示窗口,确定才进行的方法
Sep 06 Javascript
vue项目中自定义video视频控制条的实现代码
Apr 26 Javascript
浅谈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
PHP防注入安全代码
2008/04/09 PHP
CodeIgniter基本配置详细介绍
2013/11/12 PHP
php中socket的用法详解
2014/10/24 PHP
php使用正则表达式获取图片url的方法
2015/01/16 PHP
作为程序员必知的16个最佳PHP库
2015/12/09 PHP
javascript延时重复执行函数 lLoopRun.js
2007/06/29 Javascript
百度 popup.js 完美修正版非常的不错 脚本之家推荐
2009/04/17 Javascript
this和执行上下文实现代码
2010/07/01 Javascript
jQuery LigerUI 使用教程表格篇(1)
2012/01/18 Javascript
常见的原始JS选择器使用方法总结
2014/04/09 Javascript
Bootstrap每天必学之缩略图与警示窗
2015/11/29 Javascript
javascript显示倒计时控制按钮的简单实现
2016/06/07 Javascript
javascript工厂模式和构造函数模式创建对象方法解析
2016/12/30 Javascript
详解Vue 动态添加模板的几种方法
2017/04/25 Javascript
使用vux实现上拉刷新功能遇到的坑
2018/02/08 Javascript
详解如何写出一个利于扩展的vue路由配置
2019/05/16 Javascript
JavaScript数值类型知识汇总
2019/11/17 Javascript
vue实现点击按钮切换背景颜色的示例代码
2020/06/23 Javascript
javascript操作向表格中动态加载数据
2020/08/27 Javascript
django开发之settings.py中变量的全局引用详解
2017/03/29 Python
使用python的pandas库读取csv文件保存至mysql数据库
2018/08/20 Python
Django uwsgi Nginx 的生产环境部署详解
2019/02/02 Python
Pyinstaller打包.py生成.exe的方法和报错总结
2019/04/02 Python
利用Python模拟登录pastebin.com的实现方法
2019/07/12 Python
基于HTML5 Canvas 实现弹出框效果
2017/06/05 HTML / CSS
程序员机试试题汇总
2012/03/07 面试题
党支部承诺书范文
2014/03/28 职场文书
书法大赛策划方案
2014/06/04 职场文书
会议主持人开场白台词
2015/05/28 职场文书
解除处分决定书
2015/06/25 职场文书
事业单位工作人员2015年度思想工作总结
2015/10/15 职场文书
学会掌握自己命运的十条黄金法则:
2019/08/08 职场文书
Python爬取英雄联盟MSI直播间弹幕并生成词云图
2021/06/01 Python
小程序实现悬浮按钮的全过程记录
2021/10/16 HTML / CSS
Apache自带的ab压力测试工具的实现
2022/07/23 Servers
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers