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

Ale*_*hen 2 javascript

我需要动态加载JavaScript文件,然后访问其内容.

文件 test.js

test = function () {
    var pub = {}
    pub.defult_id = 1;
    return pub;
}()
Run Code Online (Sandbox Code Playgroud)


在这种情况下,它有效:

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


但我需要动态加载它,这样它不起作用:

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


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

ezt*_*tam 7

你可以这样做:

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

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


rad*_*lle 6

注意:有一种类似的解决方案,但它不会检查脚本是否已加载,并且每次都会加载脚本。这会检查 src 属性,如果已加载,则不会添加脚本标记。加载器功能:

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

用法(异步/等待):

await loadCDN("https://.../script.js")
Run Code Online (Sandbox Code Playgroud)

用途(承诺):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})
Run Code Online (Sandbox Code Playgroud)


asm*_*mud 5

www.html5rocks.com上有一篇很棒的文章值得所有感兴趣的js家伙阅读- 深入探究脚本加载的阴暗面

在那篇文章中,在考虑了许多可能的解决方案之后,作者得出结论,将js脚本添加到body元素的末尾是避免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);
});
Run Code Online (Sandbox Code Playgroud)

现在,Spec说:一起下载,全部下载后立即执行。

Firefox <3.6,Opera说:我不知道这是什么“异步”,但是恰好发生在我按照添加顺序执行通过JS添加的脚本的过程中。

Safari 5.0说:我了解“异步”,但不了解使用JS将其设置为“假”。脚本登陆后,我将以任何顺序执行它们。

IE <10说:没有关于“异步”的想法,但是有一种使用“ onreadystatechange”的解决方法。

其他一切都说:我是你的朋友,我们将按照本书的规定进行操作。

现在,使用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>');
  }
}
Run Code Online (Sandbox Code Playgroud)

稍后有一些技巧和缩小,它是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"
])
Run Code Online (Sandbox Code Playgroud)