动态加载 JavaScript 文件

Posted

技术标签:

【中文标题】动态加载 JavaScript 文件【英文标题】:Dynamically load a JavaScript file 【发布时间】:2010-09-06 11:36:47 【问题描述】:

如何可靠且动态地加载 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');
);

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

【问题讨论】:

【参考方案1】:

这是我找到的一些示例代码...有人有更好的方法吗?

  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/operaStu‌​ff/userjs/aagmfunctions.js加载脚本)【参考方案2】:

所有主要的 javascript 库,如 jscript、prototype、YUI 都支持加载脚本文件。比如在YUI中,加载完core之后可以做如下加载日历控件

var loader = new YAHOO.util.YUILoader(

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() 
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() 
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        )
     ,

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) 
        alert("error: " + YAHOO.lang.dump(o));
    

 );

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();

【讨论】:

可以看一下链接:answer【参考方案3】:

我做了与你做 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 );

【讨论】:

如果没有比 Pickle 更多的上下文,恐怕我没有任何建议。上面的代码按原样工作,它是直接从一个正常运行的项目中提取的。 @palehorse,Mike 和 Muhd,是对的,它可能适用于您的项目,但那是因为“includedFile”和“Array”变量必须在您的项目的其他地方定义,此代码独立不会运行,最好对其进行编辑,以便它可以在项目的上下文之外工作,或者至少添加一个注释来解释那些未定义的变量是什么(类型等)【参考方案4】:

谁有更好的方法?

我认为将脚本添加到正文会比将其添加到页面上的最后一个节点更容易。这个怎么样:

function include(url) 
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);

【讨论】:

我该怎么做才能让它跨域工作? (从http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js加载脚本)【参考方案5】:

我们在工作中使用的技术是使用 AJAX 请求请求 javascript 文件,然后 eval() 返回。如果您使用原型库,他们在 Ajax.Request 调用中支持此功能。

【讨论】:

【参考方案6】:

我使用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>

【讨论】:

太好了...除非您尝试加载 jquery。 看起来 jQuery 将在 jQuery Core 中加入 Require 插件以供将来发布:plugins.jquery.com/project/require 使用 jQuery 的更好方法是使用 $.getScript。看我的回答。 Modernizr (yepnope.js) 或 lab.js 是合适的解决方案。使用繁重的脚本库(必须先加载)不是移动或许多其他情况的最合适的答案。 @MaulikGangani 较旧的浏览器和 html 验证器会将其解释为结束脚本的令牌。【参考方案7】:

我已经使用了我在网上找到的另一种解决方案...这个解决方案属于创作共用,它在调用函数之前检查是否包含源代码 ...

您可以在此处找到该文件:include.js

/** include - including .js files from JS - bfults@gmail.com - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
   
  if (document.getElementsByTagName)
     
    if (!hIncludes)
    
      hIncludes = ; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    
    if (!hIncludes[sURI])
    
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    
     
 

【讨论】:

我该怎么做才能让它跨域工作? (从http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js加载脚本)【参考方案8】:

您可以使用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);
    
;

【讨论】:

我该怎么做才能让它跨域工作? (从http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js加载脚本) @user2284570 CORS ***.com/questions/5750696/… @Ciastopiekarz : 我不控制web.archive.org 然后您必须使用其他程序抓取您想要访问的数据并将其提供给自己【参考方案9】:

刚刚在YUI 3 中发现了一个很棒的功能(在撰写本文时,预览版中可用)。您可以轻松地将依赖项插入到 YUI 库和“外部”模块(您正在寻找的)中,而无需太多代码:YUI Loader。

它还回答了您关于加载外部模块后立即调用函数的第二个问题。

例子:

YUI(
    modules: 
        'simple': 
            fullpath: "http://example.com/public/js/simple.js"
        ,
        'complicated': 
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        
    ,
    timeout: 10000
).use('complicated', function(Y, result) 
    // called as soon as 'complicated' is loaded
    if (!result.success) 
        // loading failed, or timeout
        handleError(result.msg);
     else 
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    
);

非常适合我,并且具有管理依赖项的优势。请参阅 YUI 文档以获取 example with YUI 2 calendar。

【讨论】:

这可能是理想的,除了 YUI 对于这个功能来说太大了。【参考方案10】:

jquery 用它的 .append() 函数为我解决了这个问题 - 使用它来加载完整的 jquery ui 包

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library)
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    

使用 - 在你导入 jquery.js 之后,在你的 html/php/etc 的头部,你只需要像这样包含这个文件来加载你的整个库,并将它附加到头...

<script type="text/javascript" src="project.library.js"></script>

【讨论】:

【参考方案11】:

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

与此处的其他答案相比,这具有一个优势,因为您有一个内置的回调函数(以保证在相关代码运行之前加载脚本)并且您可以控制缓存。

【讨论】:

【参考方案12】:

有专门为此目的设计的脚本。

yepnope.js 内置于 Modernizr 中,lab.js 是一个更优化(但用户友好度较低的版本。

我不建议通过 jquery 或原型之类的大型库执行此操作 - 因为脚本加载器的主要优点之一是能够尽早加载脚本 - 您不必等到 jquery 和您的所有 dom在运行检查之前加载元素以查看是否要动态加载脚本。

【讨论】:

【参考方案13】:

另一个很棒的答案

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


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

 );

https://***.com/a/950146/671046

【讨论】:

我该怎么做才能让它跨域工作? (从http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js加载脚本)【参考方案14】:

javascript中没有import/include/require,但是主要有两种方式可以实现你想要的:

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

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

2 - 在 HTML 中添加带有脚本 URL 的脚本元素。

绝对是最好的方法。您甚至可以从外部服务器加载脚本,并且在您使用浏览器解析器评估代码时它很干净。您可以将script 元素放在网页的head 元素中,或者放在body 的底部。

这里讨论并说明了这两种解决方案。

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

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

例如: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
);

【讨论】:

这行得通。我希望在这里分享我的经验可以节省您的时间,因为我在这方面花了很多时间。我正在使用 Angular 6 并应用模板(html、css、jquery) 问题是模板有一个 js 文件,该文件在加载 html 元素后加载以附加侦听器事件。加载 Angular 应用程序后,该 js 文件很难执行。将其添加到 angular app ( angular.json) 脚本标签将捆绑它们,但在加载后不会执行该 js 文件。用打字稿重写的代码太多了,所以这很有帮助。下一条评论因为评论长度我会放例子 我只是简单地使用了如下代码: ngAfterViewInit() debugger; $.getScript("/assets/js/jquery.app.js", function() alert("Script loaded and executed."); // 这里你可以使用你在加载的脚本中定义的任何东西 ); 对于角度中的“$”,我遵循了这个:***.com/questions/32050645/… 这篇文章的部分内容似乎抄袭自***.com/a/950146/6243352 它有效。最后我用它解决了我的 CSP 随机数问题。谢谢。【参考方案15】:

我编写了一个简单的模块,它可以自动执行在 JavaScript 中导入/包含模块脚本的工作。试一试,请留下一些反馈! :) 代码的详细解释参考这篇博文:http://stamat.wordpress.com/2013/04/12/javascript-require-import-include-modules/

var _rmod = _rmod || ; //require module namespace
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = ;
_rmod.loading = 
    scripts: ,
    length: 0
;

_rmod.findScriptPath = function(script_name) 
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) 
        if (script_elems[i].src.endsWith(script_name)) 
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        
    
    return '';
;

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark the root directory of your library, any library


_rmod.injectScript = function(script_name, uri, callback, prepare) 

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() 
            callback(script_name, uri);
        ;

    document.getElementsByTagName('head')[0].appendChild(script_elem);
;

_rmod.requirePrepare = function(script_name, uri) 
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
;

_rmod.requireCallback = function(script_name, uri) 
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
;

_rmod.onReady = function() 
    if (!_rmod.LOADED) 
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++)
            _rmod.on_ready_fn_stack[i]();
        );
        _rmod.LOADED = true;
    
;

//you can rename based on your liking. I chose require, but it can be called include or anything else that is easy for you to remember or write, except import because it is reserved for future use.
var require = function(script_name) 
    var np = script_name.split('.');
    if (np[np.length-1] === '*') 
        np.pop();
        np.push('_all');
    

    script_name = np.join('.');
    var uri = _rmod.libpath + np.join('/')+'.js';
    if (!_rmod.loading.scripts.hasOwnProperty(script_name) 
     && !_rmod.imported.hasOwnProperty(script_name)) 
        _rmod.injectScript(script_name, uri, 
            _rmod.requireCallback, 
                _rmod.requirePrepare);
    
;

var ready = function(fn) 
    _rmod.on_ready_fn_stack.push(fn);
;

// ----- USAGE -----

require('ivar.util.array');
require('ivar.util.string');
require('ivar.net.*');

ready(function()
    //do something when required scripts are loaded
);

【讨论】:

【参考方案16】:

如果要加载 SYNC 脚本,则需要将脚本文本直接添加到 HTML HEAD 元素。添加它会触发 ASYNC 加载。要从外部文件同步加载脚本文本,请使用 XHR。下面是一个快速示例(它在这篇文章和其他帖子中使用了其他答案的一部分):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) 
Array.prototype.contains = function (obj) 
    var i = this.length;
    while (i--)  if (this[i] === obj) return true; 
    return false;
;
;

/*define object that will wrap our logic*/
var ScriptLoader = 
LoadedFiles: [],

LoadFile: function (url) 
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () 
        if (xhr.readyState === 4) 
            if (xhr.status === 200) 
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
             else 
                if (console) console.error(xhr.statusText);
            
        
    ;
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
,

AddScript: function (code) 
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);

;

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function ()  alert($('#tb1').val()); );

【讨论】:

【参考方案17】:

保持美观、简短、简单和可维护! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)

    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        
            switch(ajax.status)
            
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            
        
    ;
    ajax.send(null);


 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)

    for (i = 0; i < s.length; i++)
    
        load(s[i]);
    

此代码只是一个简短的功能示例,可能需要额外的特性功能才能在任何(或给定)平台上获得全面支持。

【讨论】:

【参考方案18】:

我迷失在所有这些示例中,但今天我需要从我的主 .js 加载一个外部 .js,我这样做了:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");

【讨论】:

可以看一下链接:answer【参考方案19】:

Here 是一个简单的带有回调和 IE 支持的:

function loadScript(url, callback) 

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState)  //IE
        script.onreadystatechange = function () 
            if (script.readyState == "loaded" || script.readyState == "complete") 
                script.onreadystatechange = null;
                callback();
            
        ;
     else  //Others
        script.onload = function () 
            callback();
        ;
    

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);


loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () 

     //jQuery loaded
     console.log('jquery loaded');

);

【讨论】:

【参考方案20】:

我知道我对这个问题的回答有点晚了,但是,www.html5rocks.com 上有一篇很棒的文章 - Deep dive into the murky waters of script loading。

在那篇文章中得出的结论是,在浏览器支持方面,在不阻塞内容呈现的情况下动态加载 JavaScript 文件的最佳方式是以下方式:

考虑到您有四个名为 script1.js, script2.js, script3.js, script4.js 的脚本,那么您可以使用 应用 async = false

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) 
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
);

现在,规范说:一起下载,全部下载后按顺序执行。

Firefox 我不知道这个“异步”的东西是什么,但碰巧我按照添加的顺序执行通过 JS 添加的脚本。 p>

Safari 5.0 说:我理解“异步”,但不理解用 JS 将其设置为“false”。我会在脚本落地后立即执行你的脚本,无论顺序如何。

IE 不知道“异步”,但有一个使用“onreadystatechange”的解决方法。

其他一切都在说:我是你的朋友,我们会照本宣科。

现在,使用 IE

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() 
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') 
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  


// loop through our script urls
while (src = scripts.shift()) 
  if ('async' in firstScript)  // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  
  else if (firstScript.readyState)  // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  
  else  // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  

一些技巧和缩小后,它是 362 字节

!function(e,t,r)function n()for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])

【讨论】:

我正在使用上述方法,它在 chrome 和 firefox 中运行良好,但在 IE 浏览器中遇到问题【参考方案21】:

这样的……

<script>
     $(document).ready(function() 
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     );
</script>

【讨论】:

【参考方案22】:

这里是一个加载 JS 文件的函数的简单示例。相关点:

您不需要 jQuery,因此您最初也可以使用它来加载 jQuery.js 文件 它与回调异步 它确保它只加载一次,因为它保存了一个包含加载 url 记录的附件,从而避免使用网络 与 jQuery $.ajax$.getScript 相反,您可以使用 nonces,从而解决 CSP unsafe-inline 的问题。只需使用属性script.nonce
var getScriptOnce = function() 

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback)
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1)

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function()
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) 
                    done=true;
                    if (typeof callback === 'function') 
                        callback();
                    
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                
            ;

            head.appendChild(script);
        
    ;
();

现在你可以简单地使用它

getScriptOnce("url_of_your_JS_file.js");

【讨论】:

【参考方案23】:

有一个新提议的 ECMA 标准,称为 dynamic import,最近并入 Chrome 和 Safari。

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule

【讨论】:

【参考方案24】:

一个荒谬的单行,对于那些认为加载 js 库不应该超过一行代码的人:P

await new Promise((resolve, reject) => let js = document.createElement("script"); js.src="mylibrary.js"; js.onload=resolve; js.onerror=reject; document.body.appendChild(js));

显然如果你要导入的脚本是一个模块,你可以使用import(...)函数。

【讨论】:

【参考方案25】:

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))
)()

它也适用于任何类型的非模块库,在这种情况下,该库在窗口对象上可用,这是旧的方式,但只能按需使用,这很好。

以suncalc.js为例,服务器必须有CORS enabled才能这样工作!

(async () => 
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then(function()
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.goldenHour.getMinutes() + ". Take your pics!")
  )
)()

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

【讨论】:

这适用于过去两年收到更新的所有浏览器,并且比以前需要的 【参考方案26】:

我已经用工作示例调整了上面的一些帖子。 在这里我们也可以将 css 和 js 放在同一个数组中。

$(document).ready(function()

if (Array.prototype.contains === undefined) 
Array.prototype.contains = function (obj) 
    var i = this.length;
    while (i--)  if (this[i] === obj) return true; 
    return false;
;
;

/* define object that will wrap our logic */
var jsScriptCssLoader = 

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) 
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) 
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) )
                $.get(url, function(data) 
                    self.addScript(data);
                );

            else if( self.cssExpr.test( url ) )
                $.get(url, function(data) 
                    self.addCss(data);
                );
            

            self.loadedFiles.push(url);
    );

    // don't load twice accross different arrays

,
addScript: function (code) 
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
,
addCss: function (code) 
    var oNew = document.createElement("style");
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);


;


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
);

【讨论】:

【参考方案27】:

使用 Promises,您可以像这样简化它。 加载器功能:

  const loadCDN = src =>
    new Promise((resolve, reject) => 
      if (document.querySelector(`head > script[src="$src"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    )

用法(异步/等待):

await loadCDN("https://.../script.js")

用法(承诺):

loadCDN("https://.../script.js").then(res => ).catch(err => )

注意:有一个类似的解决方案,但它不检查脚本是否已经加载并且每次都加载脚本。这个检查 src 属性。

【讨论】:

【参考方案28】:

对于那些喜欢单线的人:

import('./myscript.js');

您可能会遇到错误,例如:

从源访问“http://..../myscript.js”处的脚本 'http://127.0.0.1' 已被 CORS 策略阻止:否 请求中存在“Access-Control-Allow-Origin”标头 资源。

在这种情况下,您可以回退到:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());

【讨论】:

是否有理由使用Function 构造函数而不是eval?从 MDN 文档Function 中,只需创建一个函数并获取参数名称,然后将函数体作为参数。而eval 只接受一串代码,这就是您在 .js 文件中所拥有的。 @MichaelHoffmann,据我记得,我在某处读到 Function 比 eval 更安全,但我必须再次检查。 这对本地很有用,但否则可能会造成一些 XSS 弱点。在这种情况下,evalFunction 都不安全。【参考方案29】:

尽管我很喜欢 JQuery 方法的便捷性,但 JavaScript 方法并没有那么复杂,但只需要对您已经使用的方法进行一些调整...... 这是我动态加载 JS 的方法(仅在需要时),并在执行依赖于它们的脚本之前等待它们加载。

JavaScript 方法

//Create a script element that will load
let dynamicScript = document.createElement('script');

//Set source to the script we need to load
dynamicScript.src = 'linkToNeededJsFile.js';

//Set onload to callback function that depends on this script or do inline as shown below
dynamicScript.onload = () => 

    //Code that depends on the loaded script should be here

;

//append the created script element to body element
document.body.append(dynamicScript);

还有其他方法可以使用 JS 完成此任务,但我更喜欢这种方法,因为它需要每个开发人员都可以关联的基本 JS 知识。

不是答案的一部分,但这是我喜欢的 JQuery 版本,用于已经包含 JQuery 的项目:

$.getScript('linkToNeededJsFile.js', () => 

    //Code that depends on the loaded script should be here

);

更多关于 JQuery 选项here

【讨论】:

以上是关于动态加载 JavaScript 文件的主要内容,如果未能解决你的问题,请参考以下文章

两种动态加载JavaScript文件的方法

动态加载JavaScript文件

命名空间动态加载的 javascript 文件的内容

JavaScript 通过Javascript动态加载Css文件

JavaScript_动态加载CSS和JS文件

动态加载JavaScript