前言
随着项目功能越来越完善,js文件多了又多,如何通过一个init.js去管理项目所需要的js(重点:解决js文件顺序加载的问题),本文章为你揭秘。
踩坑
当前的项目状况
a.html使用的js部分
<script type="text/javascript" src="../jquery.js"></script><!--jquery类库-->
<script type="text/javascript" src="../jquery-x1.js"></script><!--某jquery插件-->
<script type="text/javascript" src="../jquery-x2.js"></script>
<script type="text/javascript" src="../jquery-x3.js"></script>
<script type="text/javascript" src="../app-core.js"></script><!--项目框架类库-->
<script type="text/javascript" src="../app-api.js"></script><!--项目框架类库-->
<script type="text/javascript" src="../page-a-control.js"></script><!--当前页面的控制-->
b.html使用的js部分
<script type="text/javascript" src="../jquery.js"></script><!--jquery类库-->
<script type="text/javascript" src="../jquery-x1.js"></script><!--某jquery插件-->
<script type="text/javascript" src="../jquery-x2.js"></script>
<script type="text/javascript" src="../jquery-x3.js"></script>
...
<script type="text/javascript" src="../app-core.js"></script><!--项目框架类库-->
<script type="text/javascript" src="../app-api.js"></script><!--项目框架类库-->
...
<script type="text/javascript" src="../page-b-control.js"></script><!--当前页面的控制-->
通过观察可以发现a.html和b.html的区别就是,除了最后的一个用于控制当前页面逻辑的js文件之外,其他的大体都一样,我这边只是举了一个简单的例子,真实项目的复杂度还是需要根据项目而定。
为了解决这个问题,我想了以下方案:
- 合并页面中相同的js生成一个js文件。后来考虑了一下项目的复杂度(合并复杂度,以及代码冗余),觉得这不是解决问题的根本。
- 使用module体系,这样改动的复杂度更高。
- 使用一个init.js,去动态创建script标签加载页面所需要用的js。
最后还是决定使用init.js去动态加载js来控制。
出坑之路
使用init.js解决该问题,瞬间想到以下思路,看代码:
init.js
var initlist=[
"../jquerymin.js",
"../jquery.x1.js",
"../jquery.x2.js",
"../jquery.x3.js",
"../core-1.js",
"../model-api.js"
];
function loadjs (jsFiles) {
var ele;
for(var init_key in jsFiles){
ele = document.createElement('script');
ele.src =initlist[init_key];//+'?r=' + Math.random() * 999999 加上随机数可以防止缓存
document.getElementsByTagName("head")[0].appendChild(ele);
}
}
function initMain(jsFiles,func){
loadjs(initlist);//1,加载框架
loadjs(jsFiles);//2,根据每个页面的特性动态加载制定的js
func();
}
使用方式:
<script type="text/javascript" src="../init.js"></script>
<script type="text/javascript">
initMain(["../page-a-control.js"],function(){
console.log("加载完成");
});
</script>
然而当我写完这个代码感觉大功告成的时候,测试总是报类未定义,排错时发现有时候可以运行成功,有时候却失败。很费解,创建出来的script标签都是有顺序的,然而却出现类未定义。后来度娘了一下,才知道这种方式创建出来的script标签,加载js的方式都是异步的,所以会出现以上问题。并且度娘上的解决方式我找了一大堆都是使用ajax方式,很多也很扯,根本不能解决。ajax方式的确可以,但是我们的网页需要能够离线使用就是直接使用file方式就能成功打开,ajax方式并不符合要求,并且我个人也不是太倾向于用ajax方式。
so,以上方案,pass
阅读了大量文档后:
最后解决方案就是创建script对象,然后在onload回调中去加载下一个js,以达到同步加载效果。结合最近学习的es6/es7语法,完美解决问题,代码如下:
init.js
var initlist=[
"../jquerymin.js",
"../jquery.x1.js",
"../jquery.x2.js",
"../jquery.x3.js",
"../core-1.js",
"../model-api.js"
];//需要加载的列表,这里可以多配置几个,根据不同需求加载不同的列表
var loadjs=(key)=>new Promise((resolve, reject)=>{
var script = document.createElement('script');;
script.src =key ;//+'?r=' + Math.random() * 9999
script.onload=()=>{
resolve();
};
document.getElementsByTagName("head")[0].appendChild(script);
});
//es6语法
function initMain(jsFiles,func){
var tasks=[];
initlist=initlist.concat(jsFiles);
for(var init_key in initlist){
tasks.push(loadjs(initlist[init_key]));
}
Promise.all(tasks).then(()=>{
func();
});
}
//es7语法,浏览器兼容不是太好,慎用
async function initMain(jsFiles,func){
initlist=initlist.concat(jsFiles);
for(var init_key in initlist){
await loadjs(initlist[init_key]);
}
await func();
}
使用方式:
<script type="text/javascript" src="../init.js"></script>
<script type="text/javascript">
initMain(["../page-a-control.js"],()=>{
console.log("加载完成");
$(()=>{ //使用init.js,jquery需要在回调里边使用
alert("加载完成");
})
});
</script>
最后成功的使用了init.js对项目所使用的js进行了管理,使项目结构变得清晰。
重点总结
onload事件的运用,除了body有此事件,常见的img,script标签 等都具有该事件
Promise对象极大的提高了异步事件的灵活性和代码的可阅读性。