关于js延迟加载(异步操作)的方式
一、概述
最近重新开始学习js,在第一章的一个小节里写到了“脚本调用策略”,书上写的这部分不多,但是发现在我之前的(笔)面试中,问到的频率还是比较高的。自己一直习惯于直接把所有js文件写在head里,后来了解到优化后,会把js放在最底部,但并不太懂这样做的好处,而且其他的一些处理方式,自己也并未有过实际的操作。
在面试中对这部分的考察,主要考察的是程序的性能方面。程序的性能是一个项目不断地追求的,通常也是项目完成后需要长期做的一件事情,为了让用户的体验更好。性能优化的核心思想就是快,可以预先准备数据(如缓存的使用),可以按需获取,可以分段、异步获取等都是常见的优化手段。
而对于js的优化(关于js的延迟加载)的好处是有助于提高页面加载速度,js延迟加载就是等页面加载完成之后在加载js文件。
之所以要优化是因为HTML元素是按其在页面中出现的次序调用的,如果用javascript来管理页面上的元素(使用文档对象模型dom),并且js加载于欲操作的HTML元素之前,则代码将出错。也就是说,我们写了js语句来获取DOM对象,但由于DOM结构还没有加载完成,因此获取到的是空对象。
示例:
<head>
<script type="text/javascript">
var ul = document.getElementsByTagName('ul')[0]; //获取ul
var list = ul.getElementsByTagName('li');
for(var i =0;i<list.length;i++){
ul.appendChild(document.createElement('li'));
}
</script>
</head>
<body>
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
</body>
</html>
上面的代码猛一看好像没啥问题,因为我们总是很习惯这样做,但是运行后发现控制台报错。

这就是因为js加载执行于DOM结构之前,所以获取不到。简单的解决办法是把<script>放在<body>后面。
二、js的同步加载和异步加载
同步加载,又称阻塞模式,是我们平时使用最多的方式,也就是直接将<script>写在<head>里。这种方式会阻止浏览器的后续处理,停止后续的解析,直到当前的加载完成。一般来说,同步加载是安全的,但如果我们js里设计到document内容输出、获取或修改DOM结构等行为,就会产生页面阻塞代码出错。所以一般就会建议把<script>写在页面最底部,以减少页面阻塞。(这种方式可能也是我们刚开始接触到js优化,最常使用的一种方式。)
异步加载,又称为非阻塞加载,在浏览器下载执行js的同时,还会继续后续页面的处理。这里也是一般面试会问到的一点,即js延迟加载的方式有哪些?
三、js延迟(异步)加载的六种方式
一般有六种方式;defer属性、async属性、动态创建dom方式、使用jquery的getScript方法、使用setTimeout延迟方法、让js最后加载。
写的是六种方式,实际上自己在项目中真实用到的也就是让js最后加载。所以对这所谓的六种方式,可能仅作为一种知识储备,当以后的项目有这种问题需求了,可以有不同的解决思路。
(一)defer属性
HTML 4.01为 <script>标签定义了defer属性(延迟脚本的执行)。
其用途是:表明脚本在执行时不会影响页面的构造,浏览器会立即下载,但延迟执行,即脚本会被延迟到整个页面都解析完毕之后再执行。
defer属性只适用于外部脚本文件,只有 Internet Explorer 支持 defer 属性。
并且defer属性解决了async引起的脚本顺序问题(见async的缺点),使用defer属性,脚本将按照在页面中出现的顺序加载和运行。
示例1:
//脚本1
<script defer src="js/vendor/jquery.js"></script>
//脚本2
<script defer src="js/script2.js"></script>
//脚本3
<script defer src="js/script3.js"></script>
上述代码添加 defer 属性,脚本将按照在页面中出现的顺序加载,因此可确保脚本1必定加载于脚本2和 脚本3之前,同时脚本2必定加载于脚本3之前。(补充:最近在看《高级程序设计》里面说,实际上延迟脚本并不一定会按照顺序执行,也不一定会在DOMContenterLoaded事件触发前执行,因此最好只包含一个延迟脚本)
示例2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>defer练习</title>
</head>
<script type="text/javascript" defer="defer" src="example1.js"></script>
<script type="text/javascript" defer="defer" src="example2.js"></script>
<body>
<!--这里是内容-->
</body>
</html>
在上面的例子中,虽然把<script>放在里<head>里,但是包含的脚本会延迟到整个页面之后,即延迟到浏览器遇到</html>标签后再执行。同时,在XHTML文档中,要把defer属性设置为defer="defer"
(二)async属性
HTML 5为 <script>标签定义了async属性。添加此属性后,脚本和HTML将一并加载(异步),代码将顺利运行。
浏览器遇到async脚本时不会阻塞页面渲染,而是直接下载然后运行。但这样的问题是,不同脚本运行次序就无法控制,只是脚本不会阻止剩余页面的显示。
async属性只适用于外部脚本文件。
示例:
//脚本1
<script async src="js/vendor/jquery.js"></script>
//脚本2
<script async src="js/script2.js"></script>
//脚本3
<script async src="js/script3.js"></script>
上述代码添加async 属性,这三者的调用顺序是不确定的,脚本1可以在脚本2和脚本3之前会之后调用,这是完全不确定的。如果脚本2和脚本3需要依赖脚本1中的函数,那么不确定的调用顺序会导致错误。(补充:异步脚本一定会在页面的load事件前执行,但可能会在DOMContenterLoaded事件触发之前或之后执行)
所以,当页面的不同脚本之间彼此独立,且不依赖于本页面的其他任何脚本时,async是最理想的选择。
总结:defer和async的异同点
相同:
- 加载文件时不会阻塞页面渲染
- 对于内部的js不起作用
- 使用这两个属性的脚本中不能调用document.write方法
区别:
- 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用
async。也就是每一个async属性的脚本都在它下载结束之后立即执行,同时会在window的load事件之前执行。 - 如果脚本需要等待解析,且依赖于其它脚本,调用这些脚本时应使用
defer,将关联的脚本按所需顺序置于 HTML 中。
(三)动态创建DOM方式
//这些代码应被放置在</body>标签前(接近HTML文件底部)
<script type="text/javascript">
function downloadJSAtOnload(){
var element = document.createElement("script");
element.src = "defer.js";
document.body.appendChild(element);
}
if (window.addEventListener) //添加监听事件
window.addEventListener("load",downloadJSAtOnload,false); //事件在冒泡阶段执行
else if (window.attachEvent)
window.attachEvent("onload",downloadJSAtOnload);
else
window.onload =downloadJSAtOnload;
</script>
补充知识点:addEventListener与attachEvent
(四)使用jquery的getScript方法
getScript() 方法通过 HTTP GET 请求载入并执行 JavaScript 文件。
语法:
jQuery.getScript(url,success(response,status))
url(必写):将要请求的 URL 字符串
success(response,status)(可选):规定请求成功后执行的回调函数。
其中的参数
response - 包含来自请求的结果数据
status - 包含请求的状态("success", "notmodified", "error", "timeout" 或 "parsererror")
//加载并执行 test.js:
$.getScript("test.js");
//加载并执行 test.js ,成功后显示信息
$.getScript("test.js", function(){
alert("Script loaded and executed.");
});
(五)使用setTimeout延迟方法
目的:延迟加载js代码,给网页加载留出时间
<script type="text/javascript">
function A(){
$.post("/lord/login",{name:username,pwd:password},function(){
alert("Hello World!");
})
}
$(function (){
setTimeout("A()",1000); //延迟1秒
})
</script>
(六)让js最后加载
将脚本元素放在文档体的低端,这样脚本就可以在HTML解析完毕后加载了。但此方案的问题是,只有在所有HTML DOM加载完成后才开始脚本的加载/解析过程。对于有大量js代码的大型网站,可能会带来显著的性能损耗。
关于js延迟加载(异步操作)的方式的更多相关文章
- 13. js延迟加载的方式有哪些
JS延迟加载,也就是等页面加载完成之后再加载 JavaScript 文件. JS延迟加载有助于提高页面加载速度. 一般有以下几种方式: 1)defer 属性 <script src=&q ...
- JS --- 延迟加载的几种方式
标题:JS延迟加载,也就是等页面加载完成之后再加载 JavaScript 文件. JS延迟加载有助于提高页面加载速度. 1. defer 属性 HTML 4.01 为 <script>标 ...
- 【Javascript】JS的异步操作,浏览器的多线程间的协作
遇到的问题,引发了思考 今天看了一个例子,强烈引发了我对于浏览器多线程之间的操作机制.同步与异步.回调函数的兴致,代码如下: <html> <head> <title&g ...
- js延迟加载优化页面响应速度
网页打开速度是衡量网站性能的一个极为重要的指标,今天就来说说如何通过JS延迟加载的方式提高页面响应速度: JS延迟加载的 含义:即等页面加载完成之后再加载 JavaScript 文件.作用:JS延迟加 ...
- Lazyr.js – 延迟加载图片(Lazy Loading)
Lazyr.js 是一个小的.快速的.现代的.相互间无依赖的图片延迟加载库.通过延迟加载图片,让图片出现在(或接近))视窗才加载来提高页面打开速度.这个库通过保持最少选项并最大化速度. 在线演示 ...
- js延迟加载,提升网页加载速度
JS延迟加载,简单例子,不多说: 代码如下: 程序代码 <script language="JavaScript" src="" id="my& ...
- JavaScript 基础——使用js的三种方式,js中的变量,js中的输出语句,js中的运算符;js中的分支结构
JavaScript 1.是什么:基于浏览器 基于(面向)对象 事件驱动 脚本语言 2.作用:表单验证,减轻服务器压力 添加野面动画效果 动态更改页面内容 Ajax网络请求 () 3.组成部分:ECM ...
- JS对象创建常用方式及原理分析
====此文章是稍早前写的,本次属于文章迁移@2017.06.27==== 前言 俗话说"在js语言中,一切都对象",而且创建对象的方式也有很多种,所以今天我们做一下梳理 最简单的 ...
- JS类继承常用方式发展史
JS类继承常用方式发展史 涉及知识点 构造函数方式继承 1-继承单个对象 1.1 多步走初始版 1.2 多步走优化版 1.3 Object.create()方式 2-继承多个对象 2.1 遍历 Obj ...
随机推荐
- 读《深入理解jvm虚拟机》之长期存活对象进入老年代,有感!!!!
关于这一段代码 有几个不是让人很理解的地方,我一一说来. 1.Desired survivor size 524288 bytes 关于这个512KB空间是怎么来的,JVM有这样一个参数: -XX:T ...
- 按时按登录IP记录Linux所有用户操作日志的方法(附脚本)
PS:Linux用户操作记录一般通过命令history来查看历史记录,但是如果因为某人误操作了删除了重要的数据,这种情况下history命令就不会有什么作用了.以下方法可以实现通过记录登陆IP地址和所 ...
- hadoop: Shuffle过程详解 (转载)
原文地址:http://langyu.iteye.com/blog/992916 另一篇博文:http://www.cnblogs.com/gwgyk/p/3997849.html Shuffle过程 ...
- 39、apk瘦身(转载)
本文转自::Android开发中文站 » 关于APK瘦身值得分享的一些经验 从APK的文件结构说起 APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用 ...
- MFC定时关机程序的实现3-最小化到托盘栏
这个定时关机运行过后默认最小化到托盘栏最好了,不用每次都去点了. 现在来看看如何将程序显示在托盘栏. 首先在头文件里声明一个变量和一个消息响应函数 //最小化到托盘栏 //第一步,生成一个成员变量,或 ...
- Python 模块搜索路径
Python 会在什么地方寻找文件来导入模块? 使用命名为 path 变量的存储在标准 sys 模块 下的一系列目录名和 ZIP 压缩文件. 你可以读取和修改这个列表.下面是在我的 Mac 上 Pyt ...
- 【LeetCode】移除元素(Remove Element)
这道题是LeetCode里的第27道题. 题目描述: 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原 ...
- [oldboy-django][1初始django]后台管理页面的布局 + djano母版(继承html)
完善学员管理系统 - bootstrap fontawesome - 分页,路径导航,表格(class样式),消息图标(i标签),邮件图标(i标签) - 响应式导航 @media(min-width, ...
- PAT1026
要获得一个C语言程序的运行时间,常用的方法是调用头文件time.h,其中提供了clock()函数,可以捕捉从程序开始运行到clock()被调用时所耗费的时间.这个时间单位是clock tick,即“时 ...
- Mysql,phpmyadmin密码忘了怎么办
1.关闭mysql服务 # service mysql stop 如果提示mysql: unrecognized service这样的错误提示. 先查看查找mysql.server,使用:find / ...