Dynamically load a JavaScript file

后端 未结 28 3003
说谎
说谎 2020-11-22 06:56

How can you reliably and dynamically load a JavaScript file? This will can be used to implement a module or component that when \'initialized\' the component will dynamical

相关标签:
28条回答
  • 2020-11-22 07:26

    I know my answer is bit late for this question, but, here is a great article in www.html5rocks.com - Deep dive into the murky waters of script loading .

    In that article it is concluded that in regards of browser support, the best way to dynamically load JavaScript file without blocking content rendering is the following way:

    Considering you've four scripts named script1.js, script2.js, script3.js, script4.js then you can do it with applying 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);
    });
    

    Now, Spec says: Download together, execute in order as soon as all download.

    Firefox < 3.6, Opera says: I have no idea what this “async” thing is, but it just so happens I execute scripts added via JS in the order they’re added.

    Safari 5.0 says: I understand “async”, but don’t understand setting it to “false” with JS. I’ll execute your scripts as soon as they land, in whatever order.

    IE < 10 says: No idea about “async”, but there is a workaround using “onreadystatechange”.

    Everything else says: I’m your friend, we’re going to do this by the book.

    Now, the full code with IE < 10 workaround:

    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>');
      }
    }
    

    A few tricks and minification later, it’s 362 bytes

    !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"
    ])
    
    0 讨论(0)
  • 2020-11-22 07:27

    Keep it nice, short, simple, and maintainable! :]

    // 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]);
        }
    }
    

    This code is simply a short functional example that could require additional feature functionality for full support on any (or given) platform.

    0 讨论(0)
  • 2020-11-22 07:27

    There are scripts that are designed specifically for this purpose.

    yepnope.js is built into Modernizr, and lab.js is a more optimized (but less user friendly version.

    I wouldn't reccomend doing this through a big library like jquery or prototype - because one of the major benefits of a script loader is the ability to load scripts early - you shouldn't have to wait until jquery & all your dom elements load before running a check to see if you want to dynamically load a script.

    0 讨论(0)
  • 2020-11-22 07:29

    Here is a simple one with callback and IE support:

    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');
    
    });
    
    0 讨论(0)
  • 2020-11-22 07:31

    An absurd one-liner, for those who think that loading a js library shouldn't take more than one line of code :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)});
    

    Obviously if the script you want to import is a module, you can use the import(...) function.

    0 讨论(0)
  • 2020-11-22 07:32

    For those of you, who love one-liners:

    import('./myscript.js');
    

    Chances are you might get an error, like:

    Access to script at 'http://..../myscript.js' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    In which case, you can fallback to:

    fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
    
    0 讨论(0)
提交回复
热议问题