【简介】通过动态脚本的方式加载js脚本文件,利用其异步的过程实现了无阻塞脚本的效果,但是对于需要同步处理的脚本,就需要再将其同步化,或者采用我们之前的直接将脚本文件放在body尾部加载的方式。
1. 无阻塞的脚本(Noblocking Scripts)
减少JavaScript文件数量并限制HTTP请求的数量虽然能够提高加载效率,但随着Web应用的功能的丰富,所需要的JavaScript代码就越多,单个的JS文件也会使浏览器死锁一段时间。为了避免这种情况,我们需要向页面逐步加载JS文件,减少浏览器的阻塞。
通过这样的方式加载的脚本,我们称之为无阻塞的脚本(Noblocking Scripts)。
2. 动态脚本元素(Dynamic Script Elements)
通过DOM操作创建一个 <script>
标签的方式称之为动态脚本元素(Dynamic Script Elements)。
这种技术的重点在于:无论在合适启动下载脚本,文件的下载和执行都不会阻塞页面其他进程。
一般有以下三种方式实现:
第一种:document.write写入
document.write("<script src='package.js'><\/script>");
这种方法中document.write会重写页面,基本上不能用。
第二种:动态改变 <script>
的src属性
<script type="text/javascript" id="loadScript"></script>
<script type="text/javascript">
loadScript = document.getElementById("loadScript");
loadScript.src = 'package.js';
</script>
这种方式需要些两个<script>
标签,不符合我们上一讲中尽量减少<script>
标签数量的优化目标。
第三种:动态创建<script>
元素
<script type="text/javascript">
var myScript = document.createElement('script');
myScript.type = "text/javascript";
myScript.src = "package.js";
document.body.appendChild(myScript);
</script>
这种方式的动态脚本较优。
这三种方式都是异步的(脚本的加载不影响主页面程序继续往下执行),所以,如果当异步加载的脚本文件,会被主页面程序马上调用,就会无法正常执行。
比如,我们有一个package.js的脚本文件,里边有一个function,在这个函数中有一些代码,我们以一个alert弹框作为代码。
function packageFun(){
alert("成功加载");
}
在另一个demo.html中,我们通过一个button来测试这个异步的过程。
<!DOCTYPE html>
<html>
<head>
<title>动态脚本</title>
<meta charset="utf-8">
<script type="text/javascript">
function init(){
var myScript = document.createElement('script');
myScript.type = "text/javascript";
myScript.src = "package.js";
document.body.appendChild(myScript);
document.write("<input type='button' value='测试运行效果' onclick='operation()'>");
packageFun();
}
function operation() {
packageFun();
}
</script>
</head>
<body>
<input type="button" value="初始化加载" onclick="init()">
</body>
</html>
运行的结果是第一次点击button,没有alert出弹框,第二次点击document write出来的button,出现alert弹框。
原因在于,第一次点击button的时候,调用了init()方法,这个方法加载了script,这时就会引入package.js文件,在引入js文件的过程中,程序又继续执行,运行到packageFun()方法的时候,因为此时package.js文件还未加载完全,就找不到packageFun()这个方法(大家不要忘记packageFun()在package.js文件中),这个过程就是一个异步过程。
而第二次点击button的时候,执行packageFun()方法,因为在第一次点击button的时候,已经将package.js文件引入了,所以就能找到packageFUn()这个方法,所以就能alert出弹框。
如果遇到引入的js文件需要马上被调用,就需要将异步过程同步化,也就是让js文件完全加载后,再执行后面的代码。
<!DOCTYPE html>
<html>
<head>
<title>异步脚本同步化</title>
<meta charset="utf-8">
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement('script');
script.type = "text/javascript";
if (script.readyState) {
// 判断是IE浏览器
alert("IE浏览器");
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
}
} else {
// 判断是其他浏览器
alert("其他浏览器");
script.onload = function() {
callback();
}
}
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
loadScript("package.js", function(){
packageFun();
});
</script>
</head>
<body>
</body>
</html>
这段代码中,关于对IE兼容性处理的部分不做过多介绍,如果对这部分不太了解的同学,可以百度一下相关的内容。
这段代码的核心在于,封装的loadScript()方法,通过监听脚本js文件完全加载的事件后,再执行callback回调函数中的内容,而要执行package.js文件中的内容,就放在这个回调函数中,模拟实现了先加载完全脚本文件,后执行callback函数中的代码的过程。
参考资料:
[1]js动态加载脚本(http://www.cnblogs.com/zhuimengdeyuanyuan/archive/2013/03/06/2946277.html)附: 欢迎大家关注我的新浪微博 - 一点编程,了解最新动态 。