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

IT技术 javascript jquery getscript
2021-03-15 04:47:28

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

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

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

所以我的问题是,

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

谢谢!

6个回答

这是具有回调功能的 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);
}
@RubenMartinezJr。调用后设置超时几秒钟,并在成功回调时清除它;如果它触发,您可能会认为它失败了(除非您的源非常慢)。
2021-04-23 04:47:28
这次真是万分感谢。但请注意,此脚本并不是 jQuery 的 getScript 的真正替代品。第一个(次要?)问题是它在回调中不返回任何值。第二个问题是(似乎)jQuery 的 getScript 跳过了一个执行帧,据我所知。因此,回调行至少可以重写为if (!isAbort && callback) setTimeout(callback, 0).
2021-05-07 04:47:28

您可以像这样获取脚本:

(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'));
@巴斯是吧?它创建一个脚本元素,设置源,然后将其附加到 DOM 中insertBefore()你在哪里看$.get
2021-04-17 04:47:28
您将如何添加在脚本完成加载和使用此方法解析时触发的回调?
2021-04-21 04:47:28
没关系,想通了。对于那些寻找相同的人,请参见此处:stackoverflow.com/a/28002292/1329367
2021-04-27 04:47:28
你们是对的......认为这是关于 getScript() 是如何实现的。
2021-05-02 04:47:28
@Mahn 因为这个问题已经回答了,您将不得不问一个新问题,但如果/当您这样做时,我很乐意回答。
2021-05-09 04:47:28

用这个

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);
我没有投票,但我冒昧地猜测,声明一个没有 的变量var,不必要地设置type并附加到head令人厌烦的人足以失去一些代表。
2021-05-02 04:47:28

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

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

用法:

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

并且代码是 eslinted。

这很酷,但它也存在不能像 jQuery 那样跳过执行帧的问题(参见 Mahn 帖子的评论)。例如,我正在加载 Pollfish Javascript 文件(来自他们的 CDN),这会创建一些 HTML。当您console.log('script loaded')运行时,该 HTML 仍未加载它需要setTimeOut(func, 0).
2021-04-26 04:47:28
警告:script.async = 'async';script.async = 1;
2021-05-07 04:47:28
我最终使用了这个选项,而且效果很好。虽然我确实必须在开始时添加一些额外的工作来记录source已经请求的内容,并在数组中跟踪他们的结果Promise,这样任何 JS 试图偶然加载相同的源脚本,都会得到浏览器已经在处理(或者可能已经完成加载)的相同Promise。
2021-05-12 04:47:28

这里有一些很好的解决方案,但许多已经过时了。@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();
}

现在我们有了一个替代品$.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;
    }
}
请注意,由于这使用 AJAX (fetch/XMLHttpRequest) 作为请求,您将绑定到现代浏览器中的 CORS 策略developer.mozilla.org/en-US/docs/Web/HTTP/CORS
2021-05-11 04:47:28