【问题标题】:How to dynamically load and use/call a JavaScript file?如何动态加载和使用/调用 JavaScript 文件?
【发布时间】:2017-04-10 01:22:55
【问题描述】:

我需要动态加载一个 JavaScript 文件,然后访问它的内容。

文件test.js

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


在这种情况下它可以工作:

<!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>


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

<!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>


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

【问题讨论】:

  • 为什么需要location.href?仅定位/test.js 将与location.href+'/test.js 相同,除了如果客户端访问并且当前网址以/ 结尾的问题,例如www.domain.com/,那么您的javascript 将定位www.domain.com//test.js
  • 真的不需要location.href。我已经变了。
  • 这个问题是相关的superuser.com/questions/1460015/…

标签: javascript


【解决方案1】:

注意:有一个类似的解决方案,但它不检查脚本是否已经加载并且每次都加载脚本。这一项检查 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
    })

用法(异步/等待):

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

用法(承诺):

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

【讨论】:

  • 所以如果我理解正确,这会检查该脚本是否已经加载到页面上,如果是,它不会创建另一个脚本并第二次加载库?有点像缓存?
  • @Limpuls 准确。
【解决方案2】:

www.html5rocks.com - Deep dive into the murky waters of script loading 中有一篇很棒的文章值得所有对 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);
});

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

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"
])

【讨论】:

    【解决方案3】:

    你可以这样做:

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

    更多信息请阅读这篇文章:https://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/

    【讨论】:

    【解决方案4】:

    动态加载 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);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 2018-07-29
      • 2011-08-15
      • 2010-09-23
      相关资源
      最近更新 更多