在我的项目中,我使用非常标准的loadScript方法来动态加载JS脚本,一旦完成,就会触发回调:
app.utilities.loadScript = function loadScript(url, callback)
{
if (app.scripts[url]===true) {
callback();
} else {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function() {
app.scripts[url] = true;
callback();
}
// Fire the loading
head.appendChild(script);
}
};
在我的项目情况下,我正在使用基于组件的系统。要说明问题,请在同一页面上设想两个Google地图组件。两者都是组件的实例。在他们的构造函数中,我调用了这个方法,就像这样...
constructor(selector) {
super(selector);
// API load status
this._api = false;
// load the maps API
app.utilities.loadScript('https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + app.config.mapsKey, this.initMap.bind(this));
}
我预料到如果组件的两个实例加载相同的脚本,则不应加载两次。出于这个原因,您可以看到loadScript助手内部如何维护已加载脚本的全局数组:app.scripts [url] = true;
但是,此策略不起作用,因为多个loadScript调用彼此非常接近地启动。由于第一个未完成(正在进行中),第二个呼叫开始另一个。
我考虑立即设置" loading"全局数组中脚本的状态,但这仍然让我想知道第二个调用如何能够听到第一个调用准备就绪,因为它确实需要这样的触发器,否则回调会很快触发。
我有一种感觉,我或许可以过度思考这个?
编辑,包括解决此问题的代码,基于@siam的回答,添加了一些调整:
app.utilities.loadScriptEvent = function loadScript(url, eventName)
{
if (app.scripts[url] == 'loading') {
return true;
}
else {
app.scripts[url] = 'loading';
var event = new CustomEvent(eventName);
// Adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
//script.onreadystatechange = callback;
script.onload = function() {
app.scripts[url] = true;
app.window.dispatchEvent(event);
}
// Fire the loading
head.appendChild(script);
}
};
请注意,该方法现在名为loadScriptEvent,第二个参数是事件名称,而不是回调。这个想法是,尽管两个组件几乎可以同时调用它,但它们都会听同一个单一事件。但这还不够,如果第一个请求已经忙于加载脚本,那么第二个请求仍然必须停止。我已经意识到加载状态。您可以这样称呼此方法:
app.utilities.loadScriptEvent('https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + app.config.mapsKey, "gmloaded");
app.window.addEventListener('gmloaded',this.initMap.bind(this));
这很好用。组件实例彼此不了解,但脚本仍然只是异步加载一次,并且我们可以在完成后立即运行代码。
答案 0 :(得分:1)
以下代码可能会让您大致了解如何完成此操作:
// using jQuery
app.utilities.loadScript = function loadScript(url, callback) {
...
script.onload = function(data) {
app.scripts[url] = true;
callback();
...
$(document).trigger('firstRqstCompleted', data);
}
...
}
$(document).on('firstRqstCompleted', function(e, data) {
// do stuff with first request's data
// initiate second request
});