如何可靠地动态加载 JavaScript 文件?这可用于实现一个模块或组件,当“初始化”时,该组件将按需动态加载所有需要的 JavaScript 库脚本。
使用该组件的客户端不需要加载所有实现该组件的库脚本文件(并手动将 <script>
标记插入其网页) - 只需加载“主”组件脚本文件即可。
主流 JavaScript 库是如何做到这一点的(Prototype、jQuery 等)?这些工具是否将多个 JavaScript 文件合并到一个脚本文件的一个可再分发的“构建”版本中?或者他们是否对辅助“库”脚本进行任何动态加载?
这个问题的补充:有没有办法在加载动态包含的 JavaScript 文件后处理该事件?原型具有用于文档范围事件的 document.observe
。例子:
document.observe("dom:loaded", function() {
// initially hide all containers for tab content
$$('div.tabcontent').invoke('hide');
});
脚本元素有哪些可用事件?
您可以使用 Prototypes 动态创建脚本元素:
new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
这里的问题是我们不知道外部脚本文件何时完全加载。
我们经常希望我们的依赖代码在下一行,并且喜欢编写如下内容:
if (iNeedSomeMore) {
Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
myFancyMethod(); // cool, no need for callbacks!
}
有一种无需回调即可注入脚本依赖项的聪明方法。您只需通过同步 AJAX 请求提取脚本并在全局级别评估脚本。
如果您使用 Prototype,则 Script.load 方法如下所示:
var Script = {
_loadedScripts: [],
include: function(script) {
// include script only once
if (this._loadedScripts.include(script)) {
return false;
}
// request file synchronous
var code = new Ajax.Request(script, {
asynchronous: false,
method: "GET",
evalJS: false,
evalJSON: false
}).transport.responseText;
// eval code on global level
if (Prototype.Browser.IE) {
window.execScript(code);
} else if (Prototype.Browser.WebKit) {
$$("head").first().insert(Object.extend(
new Element("script", {
type: "text/javascript"
}), {
text: code
}
));
} else {
window.eval(code);
}
// remember included script
this._loadedScripts.push(script);
}
};
javascript 中没有 import / include / require ,但是有两种主要方法可以实现您想要的:
1 - 您可以使用 AJAX 调用加载它,然后使用 eval。
这是最直接的方法,但由于 Javascript 安全设置,它仅限于您的域,并且使用 eval 为错误和黑客打开了大门。
2 - 在 HTML 中添加带有脚本 URL 的脚本元素。
绝对是最好的方式。您甚至可以从外部服务器加载脚本,并且在您使用浏览器解析器评估代码时它很干净。您可以将 script
元素放在网页的 head
元素中,或放在 body
的底部。
这两种解决方案都在此处进行了讨论和说明。
现在,有一个大问题你必须知道。这样做意味着您远程加载代码。现代网络浏览器将加载文件并继续执行您当前的脚本,因为它们异步加载所有内容以提高性能。
这意味着如果您直接使用这些技巧,您将无法在要求加载后的下一行使用新加载的代码,因为它仍在加载中。
EG : my_lovely_script.js 包含 MySuperObject
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
var s = new MySuperObject();
Error : MySuperObject is undefined
然后按 F5 重新加载页面。它有效!令人困惑...
那么该怎么办呢?
好吧,您可以使用作者在我给您的链接中建议的技巧。综上所述,对于赶时间的人,他在脚本加载时使用 en event 来运行一个回调函数。因此,您可以将使用远程库的所有代码放在回调函数中。例如:
function loadScript(url, callback)
{
// adding the script element 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 = callback;
// fire the loading
head.appendChild(script);
}
然后在 lambda 函数中加载脚本后编写要使用的代码:
var myPrettyCode = function() {
// here, do what ever you want
};
然后你运行所有这些:
loadScript("my_lovely_script.js", myPrettyCode);
好,我知道了。但是写所有这些东西很痛苦。
好吧,在这种情况下,您可以像往常一样使用出色的免费 jQuery 框架,它可以让您在一行中做同样的事情:
$.getScript("my_lovely_script.js", function() {
alert("Script loaded and executed.");
// here you can use anything you defined in the loaded script
});
我将 much less complicated version recently 与 jQuery 一起使用:
<script src="scripts/jquery.js"></script>
<script>
var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
var $head = $("head");
for (var i = 0; i < js.length; i++) {
$head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
}
</script>
它在我测试过的所有浏览器中都运行良好:IE6/7、Firefox、Safari、Opera。
更新:无 jQuery 版本:
<script>
var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
for (var i = 0, l = js.length; i < l; i++) {
document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
}
</script>
$.getScript
。看我的回答。
我做了与您做 Adam 的基本相同的事情,但稍作修改以确保我附加到 head
元素以完成工作。我只是创建了一个 include
函数(下面的代码)来处理脚本和 CSS 文件。
此函数还检查以确保脚本或 CSS 文件尚未动态加载。它不检查手动编码的值,并且可能有更好的方法来做到这一点,但它达到了目的。
function include( url, type ){
// First make sure it hasn't been loaded by something else.
if( Array.contains( includedFile, url ) )
return;
// Determine the MIME type.
var jsExpr = new RegExp( "js$", "i" );
var cssExpr = new RegExp( "css$", "i" );
if( type == null )
if( jsExpr.test( url ) )
type = 'text/javascript';
else if( cssExpr.test( url ) )
type = 'text/css';
// Create the appropriate element.
var element = null;
switch( type ){
case 'text/javascript' :
element = document.createElement( 'script' );
element.type = type;
element.src = url;
break;
case 'text/css' :
element = document.createElement( 'link' );
element.rel = 'stylesheet';
element.type = type;
element.href = url;
break;
}
// Insert it to the <head> and the array to ensure it is not
// loaded again.
document.getElementsByTagName("head")[0].appendChild( element );
Array.add( includedFile, url );
}
另一个很棒的答案
$.getScript("my_lovely_script.js", function(){
alert("Script loaded and executed.");
// here you can use anything you defined in the loaded script
});
https://stackoverflow.com/a/950146/671046
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
加载脚本)
这是我发现的一些示例代码......有人有更好的方法吗?
function include(url)
{
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
var nodes = document.getElementsByTagName("*");
var node = nodes[nodes.length -1].parentNode;
node.appendChild(s);
}
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
加载脚本)
Dynamic module import landed in Firefox 67+。
(async () => {
await import('./synth/BubbleSynth.js')
})()
使用错误处理:
(async () => {
await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()
它也适用于任何类型的非模块库,在这种情况下,该库在 window.self
对象上可用,这是旧方式,但只能按需使用,这很好。
使用 suncalc.js 的示例,服务器必须具有 CORS enabled 才能以这种方式工作!
(async () => { await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js') .then( () => { let times = SunCalc .getTimes(new Date(), 51.5,-0.1); console.log("今天伦敦黄金时段:" + times.goldenHour.getHours() + ':' + times.goldenHour.getMinutes() + "。你的照片!") }) })()
https://caniuse.com/#feat=es6-module-dynamic-import
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
加载脚本)web.archive.org
。