动态加载JavaScript文件

Ada*_*dam 158 javascript import file include

如何可靠地动态加载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');
});
Run Code Online (Sandbox Code Playgroud)

脚本元素有哪些可用事件?

aem*_*kei 79

您可以编写动态脚本标记(使用Prototype):

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
Run Code Online (Sandbox Code Playgroud)

这里的问题是我们不知道外部脚本文件何时完全加载.

我们经常希望我们的依赖代码在下一行,并喜欢写下这样的东西:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}
Run Code Online (Sandbox Code Playgroud)

有一种智能方法可以在不需要回调的情况下注入脚本依赖项.您只需通过同步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);
    }
};
Run Code Online (Sandbox Code Playgroud)


Dha*_*hah 62

javascript中没有import/include/require,但有两种主要方法可以实现您的目标:

1 - 您可以使用AJAX调用加载它,然后使用eval.

这是最直接的方式,但由于Javascript安全设置,它仅限于您的域,并且使用eval可以打开错误和黑客的大门.

2 - 在HTML中添加带脚本URL的脚本标记.

绝对是最好的方式.您甚至可以从外部服务器加载脚本,并且在使用浏览器解析器评估代码时它是干净的.您可以将标记放在网页的头部或主体的底部.

这里讨论和说明了这两种解决方案.

现在,您必须了解一个大问题.这样做意味着您远程加载代码.现代Web浏览器将加载文件并继续执行当前脚本,因为它们异步加载所有内容以提高性能.

这意味着如果你直接使用这些技巧,在你要求加载后,你将无法在下一行使用新加载的代码,因为它仍然会加载.

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
Run Code Online (Sandbox Code Playgroud)

然后你重新加载页面击中F5.它的工作原理!混乱...

那该怎么办呢?

好吧,你可以在我给你的链接中使用作者建议的黑客.总之,对于匆忙的人,他在加载脚本时使用en事件来运行回调函数.因此,您可以使用远程库将所有代码放在回调函数中.EG:

function loadScript(url, callback)
{
    // 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 = callback;

   // fire the loading
   head.appendChild(script);
}
Run Code Online (Sandbox Code Playgroud)

然后在lambda函数中加载脚本后编写要使用的代码:

var myPrettyCode = function() {
    // here, do what ever you want
};
Run Code Online (Sandbox Code Playgroud)

然后你运行所有:

loadScript("my_lovely_script.js", myPrettyCode);
Run Code Online (Sandbox Code Playgroud)

好,我知道了.但写这些东西真是太痛苦了.

那么,在这种情况下,你可以像往常一样使用梦幻般的免费jQuery框架,它让你在一行中做同样的事情:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
Run Code Online (Sandbox Code Playgroud)

  • 不敢相信这个答案是多么被低估了.谢谢. (11认同)
  • 我打赌这是因为人们不喜欢阅读第一行,除非它承诺如果他们"只是按照这三个秘密步骤取得成功"就会变得富有. (2认同)

tra*_*vis 27

最近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>
Run Code Online (Sandbox Code Playgroud)

它在我测试的每个浏览器中都运行良好:IE6/7,Firefox,Safari,Opera.

更新: jQuery-less版本:

<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>
Run Code Online (Sandbox Code Playgroud)

  • 这很好......除非你试图加载jquery. (24认同)
  • @MaulikGangani较旧的浏览器和html验证器会将其解释为结束脚本的令牌. (2认同)

pal*_*rse 20

我做的基本上与你做亚当的事情相同,但稍加修改以确保我附加到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 tag = null;
    switch( type ){
        case 'text/javascript' :
            tag = document.createElement( 'script' );
            tag.type = type;
            tag.src = url;
            break;
        case 'text/css' :
            tag = document.createElement( 'link' );
            tag.rel = 'stylesheet';
            tag.type = type;
            tag.href = url;
            break;
    }

    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( tag );
    Array.add( includedFile, url );
}
Run Code Online (Sandbox Code Playgroud)

  • @palehorse,Mike和Muhd,是对的,它可能适用于你的项目,但那是因为"includedFile"和"Array"变量必须在你的项目的其他地方定义,这个代码本身不会运行,它可能是更好地编辑它,以便它可以在项目的上下文之外工作,或者至少添加一个注释来解释那些未定义的变量是什么(类型等) (5认同)
  • includeFile未定义. (2认同)

Nav*_*eed 14

另一个很棒的答案

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });
Run Code Online (Sandbox Code Playgroud)

/sf/answers/66510251/

  • 我该怎么做才能让它适用于跨域?*(从http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu ff/userjs/aagmfunctions.js`加载脚本)* (2认同)

NVR*_*VRM 10

动态模块导入登陆 Firefox 67+

(async () => {
   await import('./synth/BubbleSynth.js')
})()
Run Code Online (Sandbox Code Playgroud)

带有错误处理:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()
Run Code Online (Sandbox Code Playgroud)

它也适用于任何类型的非模块库,在这种情况下,库可以在window.self对象上使用,旧方法,但仅按需使用,这很好。

使用suncalc.js的示例,服务器必须启用 CORS才能以这种方式工作!

(async () => {
   await import('./synth/BubbleSynth.js')
})()
Run Code Online (Sandbox Code Playgroud)

https://caniuse.com/#feat=es6-module-dynamic-import


Ada*_*dam 8

以下是我发现的一些示例代码...有没有人有更好的方法?

  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);
  }
Run Code Online (Sandbox Code Playgroud)


Muh*_*uhd 6

如果你已经加载了jQuery,你应该使用$ .getScript.

这比其他答案有一个优势,因为你有一个内置的回调函数(以保证在依赖代码运行之前加载脚本),你可以控制缓存.