简体   繁体   English

如何动态加载和使用/调用 JavaScript 文件?

[英]How to dynamically load and use/call a JavaScript file?

I need to dynamically load a JavaScript file and then access its content.我需要动态加载一个 JavaScript 文件,然后访问它的内容。

File test.js文件test.js

test = function () {
    var pub = {}
    pub.defult_id = 1;
    return pub;
}()


In this case it works:在这种情况下,它有效:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="/test.js"></script>    
</head>
<body>
    <script type="text/javascript">
        console.log(test.defult_id);
    </script>
</body>
</html>


But I need to load it dynamically, and that way it does not work:但是我需要动态加载它,这样它就不起作用:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <script type="text/javascript">
        function loadjs(file) {
            var script = document.createElement("script");
            script.type = "application/javascript";
            script.src = file;
            document.body.appendChild(script);
        }
        loadjs('test.js');
        console.log(test.defult_id);
    </script>
</body>
</html>


Error: Uncaught ReferenceError: test is not defined(…)错误: Uncaught ReferenceError: test is not defined(…)

You could do it like this:你可以这样做:

function loadjs(file) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = file;
    script.onload = function(){
        alert("Script is ready!"); 
        console.log(test.defult_id);
    };
    document.body.appendChild(script);
 }

For more information read this article : https://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/有关更多信息,请阅读本文: https : //www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/

There is a great article which is worth reading for all the guys interesting in js script loading in www.html5rocks.com - Deep dive into the murky waters of script loading . www.html5rocks.com 中有一篇很棒的文章值得所有对 js 脚本加载感兴趣的人阅读 - 深入了解脚本加载的阴暗面

In that article after considering many possible solutions, the author concluded that adding js scripts to the end of body element is the best possible way to avoid blocking page rendering by js scripts thus speeding page loading time.在那篇文章中,作者在考虑了许多可能的解决方案后得出结论,在 body 元素的末尾添加 js 脚本是避免 js 脚本阻塞页面渲染从而加快页面加载时间的最佳方法。

But, the author propose another good alternate solution for those people who are desperate to load and execute scripts asynchronously.但是,作者为那些迫切需要异步加载和执行脚本的人提出了另一种很好的替代解决方案。

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脚本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);
});

Now, Spec says : Download together, execute in order as soon as all download.现在, Spec 说:一起下载,全部下载后按顺序执行。

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. Firefox < 3.6,Opera 说:我不知道这个“异步”是什么,但碰巧我按照添加的顺序执行通过 JS 添加的脚本。

Safari 5.0 says: I understand “async”, but don't understand setting it to “false” with JS. Safari 5.0 说:我理解“async”,但不理解用 JS 将其设置为“false”。 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”. IE < 10 说:不知道“异步”,但有一个使用“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:现在,使用 IE < 10 解决方法的完整代码:

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一些技巧和缩小后,它是 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"
])

NOTE: there was one similar solution but it doesn't check if the script is already loaded and loads the script each time.注意:有一个类似的解决方案,但它不会检查脚本是否已经加载并且每次都加载脚本。 This one checks src property and doesn't add script tag if already loaded.这个检查 src 属性,如果已经加载,则不会添加脚本标记。 Loader function:装载机功能:

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

Usage (async/await):用法(异步/等待):

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

Usage (Promise):用法(承诺):

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

Dinamically loading JS files is asynchronous, so to ensure your script is loaded before calling some function inside, use the onload event in script:动态加载 JS 文件是异步的,因此为了确保在调用内部的某些函数之前加载脚本,请在脚本中使用 onload 事件:

function loadjs(file) {
            var script = document.createElement("script");
            script.type = "application/javascript";
            script.onload=function(){
                //at this tine the script is loaded
                console.log("Script loaded!");
                console.log(test);
            }
            script.src = file;
            document.body.appendChild(script);
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM