本文探讨JavaScript动态加载脚本时常见的依赖顺序问题。通过演示直接加载导致的jQuery未定义错误,分析多线程加载的时间差原因,并给出利用onload事件实现串行加载的解决方案,确保脚本按序执行。
动态加载脚本的依赖问题
在Web开发中,动态加载JavaScript文件是一个常见但容易引发问题的场景。网络上流传的一些基础做法往往忽略了脚本执行的时序问题。
function loadjs(fileurl){
var sct = document.createElement("script");
sct.src = fileurl;
document.head.appendChild(sct);
}
为了验证这种简单加载方式的效果,我们进行实际测试。
<html>
<head>
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" media="screen" />
</head>
<body>
<script>
function loadjs(fileurl){
var sct = document.createElement("script");
sct.src = fileurl;
document.head.appendChild(sct);
}
loadjs("http://code.jquery.com/jquery-1.12.0.js");
loadjs("http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js")
loadjs("http://bootboxjs.com/bootbox.js")
</script>
</body>
</html>
当代码执行完毕后,控制台会抛出如下的错误信息。

尽管jQuery脚本在代码中排在第一位被加载,但系统依然报错提示找不到jQuery对象。
造成这种现象的原因是,上述加载方式相当于同时开启了三个独立的线程。虽然jQuery线程最先启动,但其执行耗时超过了后续两个脚本线程。因此,当后续脚本执行时,jQuery可能尚未完全初始化,导致引用失败。
利用onload事件解决加载顺序
那么,应该如何正确处理这种情况呢?
关键在于理解文件加载的生命周期。脚本加载过程包含一个onload事件,该事件可用于监听文件是否加载完成。
基于这一机制,我们可以优化加载逻辑,确保前一个脚本加载并执行完毕后,再加载下一个脚本。改进后的代码如下。
<html>
<head>
<link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" media="screen" />
</head>
<body>
<script>
function loadjs(fileurl, fn){
var sct = document.createElement("script");
sct.src = fileurl;
if(fn){
sct.onload = fn;
}
document.head.appendChild(sct);
}
loadjs("http://code.jquery.com/jquery-1.12.0.js",function(){
loadjs("http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js",function(){
loadjs("http://bootboxjs.com/bootbox.js")
})
});
</script>
</body>
</html>
执行这段代码后,所有文件的加载均变为串行模式。只有在前一个脚本加载完成后,才会触发下一个脚本的加载,从而彻底解决了依赖对象找不到的问题。
实战:Bootbox.js的串行加载
接下来,我们测试一个弹出框效果,代码中使用了Bootbox.js插件。加载逻辑如下。
loadjs("http://code.jquery.com/jquery-1.12.0.js",function(){
loadjs("http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js",function(){
loadjs("http://bootboxjs.com/bootbox.js",function(){
bootbox.alert("Hello world!", function() {
Example.show("Hello world callback");
});
})
})
});
刷新页面后,即可直接看到弹出的对话框效果。

动态加载代码时,调试过程往往耗时较多。建议通过编写最简单的示例来深入理解其原理。此外,这段代码可以进一步封装,甚至加入对CSS文件的加载支持,从而形成通用的前端插件。