原生 JavaScript 中的 jQuery.getScript 替代方案

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16839698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 17:53:59  来源:igfitidea点击:

jQuery.getScript alternative in native JavaScript

javascriptjquerygetscript

提问by ILikeTacos

I'm trying to load JS scripts dynamically, but using jQuery is not an option.

我正在尝试动态加载 JS 脚本,但不能使用 jQuery。

I checked jQuery source to see how getScriptwas implemented so that I could use that approach to load scripts using native JS. However, getScript only calls jQuery.get()

我检查了 jQuery 源代码以了解getScript是如何实现的,以便我可以使用该方法使用本机 JS 加载脚本。但是,getScript 只调用 jQuery.get()

and I haven't been able to find where the get method is implemented.

我一直无法找到 get 方法的实现位置。

So my question is,

所以我的问题是,

What's a reliable way to implement my own getScript method using native JavaScript?

使用本机 JavaScript 实现我自己的 getScript 方法的可靠方法是什么?

Thanks!

谢谢!

采纳答案by Mathletics

You can fetch scripts like this:

您可以像这样获取脚本:

(function(document, tag) {
    var scriptTag = document.createElement(tag), // create a script tag
        firstScriptTag = document.getElementsByTagName(tag)[0]; // find the first script tag in the document
    scriptTag.src = 'your-script.js'; // set the source of the script to your script
    firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag); // append the script to the DOM
}(document, 'script'));

回答by Mahn

Here's a jQuery getScript alternative with callback functionality:

这是具有回调功能的 jQuery getScript 替代方案:

function getScript(source, callback) {
    var script = document.createElement('script');
    var prior = document.getElementsByTagName('script')[0];
    script.async = 1;

    script.onload = script.onreadystatechange = function( _, isAbort ) {
        if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) {
            script.onload = script.onreadystatechange = null;
            script = undefined;

            if(!isAbort && callback) setTimeout(callback, 0);
        }
    };

    script.src = source;
    prior.parentNode.insertBefore(script, prior);
}

回答by AXE

Firstly, Thanks for @Mahn's answer. I rewrote his solution in ES6 and promise, in case someone need it, I will just paste my code here:

首先,感谢@Mahn 的回答。我在 ES6 中重写了他的解决方案并承诺,如果有人需要它,我会在这里粘贴我的代码:

const loadScript = (source, beforeEl, async = true, defer = true) => {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script');
    const prior = beforeEl || document.getElementsByTagName('script')[0];

    script.async = async;
    script.defer = defer;

    function onloadHander(_, isAbort) {
      if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
        script.onload = null;
        script.onreadystatechange = null;
        script = undefined;

        if (isAbort) { reject(); } else { resolve(); }
      }
    }

    script.onload = onloadHander;
    script.onreadystatechange = onloadHander;

    script.src = source;
    prior.parentNode.insertBefore(script, prior);
  });
}

Usage:

用法:

const scriptUrl = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoad&render=explicit';
loadScript(scriptUrl).then(() => {
  console.log('script loaded');
}, () => {
  console.log('fail to load script');
});

and code is eslinted.

和代码是 eslinted。

回答by Rohit Agrawal

use this

用这个

var js_script = document.createElement('script');
js_script.type = "text/javascript";
js_script.src = "http://www.example.com/script.js";
js_script.async = true;
document.getElementsByTagName('head')[0].appendChild(js_script);

回答by m.w.

Mozilla Developer Network provides an examplethat works asynchronously and does not use 'onreadystatechange' (from @ShaneX's answer) that is not really present in a HTMLScriptTag:

Mozilla Developer Network 提供了一个异步工作的示例,并且不使用 HTMLScriptTag 中并不真正存在的“onreadystatechange”(来自@ShaneX 的回答):

function loadError(oError) {
  throw new URIError("The script " + oError.target.src + " didn't load correctly.");
}

function prefixScript(url, onloadFunction) {
  var newScript = document.createElement("script");
  newScript.onerror = loadError;
  if (onloadFunction) { newScript.onload = onloadFunction; }
  document.currentScript.parentNode.insertBefore(newScript, document.currentScript);
  newScript.src = url;
}

Sample usage:

示例用法:

prefixScript("myScript1.js");
prefixScript("myScript2.js", function () { alert("The script \"myScript2.js\" has been correctly loaded."); });

But @Agamemnus' comment should be considered: The script might not be fully loaded when onloadFunctionis called. A timer could be used setTimeout(func, 0)to let the event loop finalize the added script to the document. The event loop finally calls the function behind the timer and the script should be ready to use at this point.

但应考虑@Agamemnus 的评论:onloadFunction调用时脚本可能未完全加载。计时器可用于setTimeout(func, 0)让事件循环完成添加到文档的脚本。事件循环最终调用了定时器背后的函数,此时脚本应该可以使用了。

However, maybe one should consider returning a Promise instead of providing two functions for exception & success handling, that would be the ES6 way. This would also render the need for a timer unnecessary, because Promises are handled by the event loop - becuase by the time the Promise is handled, the script was already finalized by the event loop.

然而,也许应该考虑返回一个 Promise 而不是提供两个用于异常和成功处理的函数,这将是 ES6 的方式。这也使得不需要计时器,因为 Promise 由事件循环处理 - 因为在处理 Promise 时,脚本已经由事件循环完成。

Implementing Mozilla's method including Promises, the final code looks like this:

实现包括 Promises 在内的 Mozilla 方法,最终代码如下所示:

function loadScript(url)
{
  return new Promise(function(resolve, reject)
  {
    let newScript = document.createElement("script");
    newScript.onerror = reject;
    newScript.onload = resolve;
    document.currentScript.parentNode.insertBefore(newScript, document.currentScript);
    newScript.src = url;
  });
}

loadScript("test.js").then(() => { FunctionFromExportedScript(); }).catch(() => { console.log("rejected!"); });

回答by Xandor

There are some good solutions here but many are outdated. There is a good oneby @Mahn but as stated in a comment it is not exactly a replacement for $.getScript()as the callback does not receive data. I had already written my own function for a replacement for $.get()and landed here when I need it to work for a script. I was able to use @Mahn's solution and modify it a bit along with my current $.get()replacement and come up with something that works well and is simple to implement.

这里有一些很好的解决方案,但许多已经过时了。@Mahn有一个很好的,但正如评论中所述,它并不完全是替代品,$.getScript()因为回调不接收数据。我已经编写了自己的函数来替代$.get()并在需要它为脚本工作时登陆这里。我能够使用@Mahn 的解决方案并与我当前的$.get()替代品一起对其进行一些修改,并提出了一些运行良好且易于实现的方法。

function pullScript(url, callback){
    pull(url, function loadReturn(data, status, xhr){
        //If call returned with a good status
        if(status == 200){
            var script = document.createElement('script');
            //Instead of setting .src set .innerHTML
            script.innerHTML = data;
            document.querySelector('head').appendChild(script);
        }
        if(typeof callback != 'undefined'){
            //If callback was given skip an execution frame and run callback passing relevant arguments
            setTimeout(function runCallback(){callback(data, status, xhr)}, 0);
        }
    });
}

function pull(url, callback, method = 'GET', async = true) {
    //Make sure we have a good method to run
    method = method.toUpperCase();
    if(!(method === 'GET'   ||   method === 'POST'   ||  method === 'HEAD')){
        throw new Error('method must either be GET, POST, or HEAD');
    }
    //Setup our request
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {   // XMLHttpRequest.DONE == 4
            //Once the request has completed fire the callback with relevant arguments
            //you should handle in your callback if it was successful or not
            callback(xhr.responseText, xhr.status, xhr);
        }
    };
    //Open and send request
    xhr.open(method, url, async);
    xhr.send();
}

Now we have a replacement for $.get()and $.getScript()that work just as simply:

现在我们有了一个替代品$.get()$.getScript()它的工作方式同样简单:

pullScript(file1, function(data, status, xhr){
    console.log(data);
    console.log(status);
    console.log(xhr);
});

pullScript(file2);

pull(file3, function loadReturn(data, status){
    if(status == 200){
        document.querySelector('#content').innerHTML = data;
    }
}

回答by paulcol.

This polishes up previous ES6 solutions and will work in all modern browsers

这完善了以前的 ES6 解决方案,并将适用于所有现代浏览器

Load and Get Script as a Promise

加载和获取脚本作为 Promise

const getScript = url => new Promise((resolve, reject) => {
  const script = document.createElement('script')
  script.src = url
  script.async = true

  script.onerror = reject

  script.onload = script.onreadystatechange = function() {
    const loadState = this.readyState

    if (loadState && loadState !== 'loaded' && loadState !== 'complete') return

    script.onload = script.onreadystatechange = null

    resolve()
  }

  document.head.appendChild(script)
})

Usage

用法

getScript('https://dummyjs.com/js')
.then(() => {
  console.log('Loaded', dummy.text())
})
.catch(() => {
  console.error('Could not load script')
})

Also works for JSONP endpoints

也适用于 JSONP 端点

const callbackName = `_${Date.now()}`
getScript('http://example.com/jsonp?callback=' + callbackName)
.then(() => {
  const data = window[callbackName];

  console.log('Loaded', data)
})

Also, please be careful with some of the AJAX solutions listed as they are bound to the CORS policy in modern browsers https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

另外,请注意列出的一些 AJAX 解决方案,因为它们绑定到现代浏览器中的 CORS 策略https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS