网页性能优化之异步加载js文件
一个网页的有很多地方可以进行性能优化,比较常见的一种方式就是异步加载js脚本文件。在谈异步加载之前,先来看看浏览器加载js文件的原理。
浏览器加载 JavaScript 脚本,主要通过
<script>元素完成。正常的网页加载流程是这样的。
- 浏览器一边下载 HTML 网页,一边开始解析。也就是说,不等到下载完,就开始解析。
- 解析过程中,浏览器发现
<script>元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎。- 如果
<script>元素引用了外部脚本,就下载该脚本再执行,否则就直接执行代码。- JavaScript 引擎执行完毕,控制权交还渲染引擎,恢复解析 HTML 网页。
加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后,再继续渲染。原因是 JavaScript 代码可以修改 DOM,所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题。
上面所说的,就是我们平时最常见到的,将`<script>`标签放到`<head>`中的做法,这样的加载方式叫做同步加载,或者叫阻塞加载,因为在加载js脚本文件时,会阻塞浏览器解析HTML文档,等到下载并执行完毕之后,才会接着解析HTML文档。如果加载时间过长(比如下载时间太长),就会造成浏览器“假死”,页面一片空白。而且,放在`<head>`中同步加载的js文件中不能对DOM进行操作,否则会产生错误,因为这个时候HTML还没有进行解析,DOM还没有生成。由此看来,同步加载带来的体验往往并不好。
下面我们来看几种异步加载的方式。
1. 将<script>标签放到<body>底部
严格来说,这并不算是异步加载,但是这也是常见的通过改变js加载方式来提升页面性能的一种方式,所以也就放到这里来说。
将<script>放到<body>底部,解决上上面说到的几个问题,一是不会造成页面解析的阻塞,就算加载时间过长用户也可以看到页面而不是一片空白,而且这时候可以在脚本中操作DOM。
2. defer属性
通过给<script>标签设置defer属性,将脚本文件设置为延迟加载,当浏览器遇到带有defer属性的<script>标签时,会再开启一个线程去下载js文件,同时继续解析HTML文档,等等HTML全部解析完毕DOM加载完成之后,再去执行加载好的js文件。
这种方式只适用于引用外部js文件的<script>标签,可以保证多个js文件的执行顺序就是它们在页面中出现的顺序,但是要注意,添加defer属性的js文件不应该使用document.write方法。
3. async属性
async属性和defer属性类似,也是会开启一个线程去下载js文件,但和defer不同的时,它会在下载完成后立刻执行,而不是会等到DOM加载完成之后再执行,所以还是有可能会造成阻塞。
同样的,async也是只适用于外部js文件,也不能在js中使用document.write方法,但是对多个带有async的js文件,它不能像defer那样保证按顺序执行,它是哪个js文件先下载完就先执行哪个。
4. 动态创建<script>标签
可以通过动态地创建<script>标签来实现异步加载js文件,例如下面代码:
(function(){
var scriptEle = document.createElement("script");
scriptEle.type = "text/javasctipt";
scriptEle.async = true;
scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
var x = document.getElementsByTagName("head")[0];
x.insertBefore(scriptEle, x.firstChild);
})();
或者
(function(){
if(window.attachEvent){
window.attachEvent("load", asyncLoad);
}else{
window.addEventListener("load", asyncLoad);
}
var asyncLoad = 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);
}
})();
上面两种方法中,第一种方式执行完之前会阻止onload事件的触发,而现在很多页面的代码都在onload时还执行额外的渲染工作,所以还是会阻塞部分页面的初始化处理。第二种则不会阻止onload事件的触发。
这里要简要说明一下window.DOMContentLoaded和window.onload这两个事件的区别,前者是在DOM解析完毕之后触发,这时候DOM解析完毕,JavaScript可以获取到DOM引用,但是页面中的一些资源比如图片、视频等还没有加载完,作用同jQuery中的ready事件。后者则是页面完全加载完毕,包括各种资源。
说完了这几种常见的异步加载js脚本的方式,再来看最后一个问题,什么时候用defer,什么时候用async呢?一般来说,两者之间的选择则是看脚本之间是否有依赖关系,有依赖的话应当要保证执行顺序,应当使用defer没有依赖的话使用async,同时使用的话defer失效。要注意的是两者都不应该使用document.write,这个导致整个页面被清除。
下面一幅图表明了同步加载以及defer、async加载时的区别,其中绿色线代表 HTML 解析,蓝色线代表网络读取js脚本,红色线代表js脚本执行时间:

网页性能优化之异步加载js文件的更多相关文章
- JS性能优化之怎么加载JS文件
IE8+等实行并行下载,各JS下载不受影响,但仍阻塞其他资源下载 如: 图片 所以首要规则就是:将JS放在body底部(推荐) 加载100kb的单个文件比4个25kb的文件快(减少外链文件数量)(脚本 ...
- angularLoad(用以异步加载js文件)
angularLoad(用以异步加载js文件) 使用方法: 1.执行命令 下载 lib npm install angular-load --save 2.index.html引用js <scr ...
- 异步加载js文件的方法
# 异步加载js文件 - js的加载默认是同步的,因为js是单线程执行,只能完成一件再执行下一件. - 一些外部引入的js文件可以因为文件太大,在加载资源的过程中会影响dom元素的加载,影响了用户体验 ...
- 异步加载js文件的方法总结
方法一,jQuery.getScript HTML 代码: 代码如下 复制代码 <button id="go">Run</button><div cl ...
- 页面异步加载javascript文件
昨天听一同事说的异步加载js文件,可以提高页面加载速度.具体方法如下:(function() { var ga = document.createElement('script'); ga.type ...
- 网页加载速度优化2--先加载css,然后再加载js文件。
网页加载时,是按从上到下,从左到右的顺序加载的.所以一定要先加载css文件(不要让用户看到一个杂乱无章的页面),最后再加载js文件,js一般都是处理功能的,所以不需要提前加载.先给用户观感,再给用户上 ...
- HTML5中script的async属性异步加载JS
HTML5中script的async属性异步加载JS HTML4.01为script标签定义了5个属性: charset 可选.指定src引入代码的字符集,大多数浏览器忽略该值.defer 可 ...
- 异步加载js的三种方法
js加载时间线 : 它是根据js出生的那一刻开始记录的一系列浏览器按照顺序做的事,形容的就是加载顺序,可以用来优化什么东西,理论基础,背下来. 1.创建Document对象,开始解析web页面.解析H ...
- 异步加载js
//异步加载js function loadScript(url,callback){ var script = document.createElement("script"); ...
随机推荐
- PAT A1029 Median (25 分)——队列
Given an increasing sequence S of N integers, the median is the number at the middle position. For e ...
- Android学习之基础知识五—创建自定义控件
下面是控件和布局的继承关系: 从上面我们看到: 1.所有控件都是直接或间接继承View,所有的布局都是直接或间接继承ViewGroup 2.View是Android中最基本的UI组件,各种组件其实就是 ...
- java从命令行接受多个数字求和输出
一·设计思路 1.定义一个整型变量sum,用于接收和 2.利用循环将命令行数字求和 3.输出参数个数以及参数之和 二·流程图 三·程序源代码 public class JavaAppArguments ...
- 在SpringMVC中使用HandlerInterceptor来实现拦截器功能
需求:我们需要在请求某些特定的URL(URL格式为Restful格式)时添加拦截器,以实现进行权限控制. 如:/ResourcePlan/projectCode/P1503127828/PROJECT ...
- maven docker 插件集成的几个小坑
昨晚看springboot视频的时候,发现可以使用docker-maven-plugin这个插件直接build出 docker 镜像到远程服务器上,感觉很方便,于是自己也试了一下,但是碰到了几个问题, ...
- Mvc_扩展@html
HtmlHelper的一个实例,它被定义在System.Web.Mvc名称空间下的WebViewPage类,即它对于所有MVC页面都可用)建立好扩展方法后: @Html.CreateGanderRad ...
- Webpack 2 视频教程 002 - NodeJS 安装与配置
原文发表于我的技术博客 这是我免费发布的高质量超清「Webpack 2 视频教程」. Webpack 作为目前前端开发必备的框架,Webpack 发布了 2.0 版本,此视频就是基于 2.0 的版本讲 ...
- TRIO-basic指令--九九乘法表demo
在路上闲的没事,想到之前自己用别的语言实现乘法口诀表,于是来了兴趣用TRIO-basic试一下,挺简单的一段代码,大家看看就好. ' TRIO-basic '实现乘法口诀表 定义两个整型的局部变量 D ...
- sql-server安装
ubuntu安装sql-server https://docs.microsoft.com/zh-cn/sql/linux/quickstart-install-connect-ubuntu?view ...
- linux内核分析字符集实践报告