我正在尝试动态加载 JS 脚本,但不能选择使用 jQuery。
我检查了 jQuery 源代码以了解getScript是如何实现的,以便我可以使用该方法使用本机 JS 加载脚本。但是,getScript 只调用 jQuery.get()
而且我一直无法找到 get 方法的实现位置。
所以我的问题是,
使用本机 JavaScript 实现我自己的 getScript 方法的可靠方法是什么?
谢谢!
我正在尝试动态加载 JS 脚本,但不能选择使用 jQuery。
我检查了 jQuery 源代码以了解getScript是如何实现的,以便我可以使用该方法使用本机 JS 加载脚本。但是,getScript 只调用 jQuery.get()
而且我一直无法找到 get 方法的实现位置。
所以我的问题是,
使用本机 JavaScript 实现我自己的 getScript 方法的可靠方法是什么?
谢谢!
这是一个具有回调功能的 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);
}
您可以像这样获取脚本:
(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'));
用这个
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);
首先,感谢@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);
});
}
用法:
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的。
这完善了以前的 ES6 解决方案,并且适用于所有现代浏览器
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)
})
getScript('https://dummyjs.com/js')
.then(() => {
console.log('Loaded', dummy.text())
})
.catch(() => {
console.error('Could not load script')
})
也适用于 JSONP 端点
const callbackName = `_${Date.now()}`
getScript('http://example.com/jsonp?callback=' + callbackName)
.then(() => {
const data = window[callbackName];
console.log('Loaded', data)
})
此外,请注意列出的一些 AJAX 解决方案,因为它们与现代浏览器中的 CORS 策略绑定在一起https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
这里有一些很好的解决方案,但很多都已经过时了。@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;
}
}
Mozilla 开发者网络提供了一个异步工作的示例,并且不使用 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;
}
示例用法:
prefixScript("myScript1.js");
prefixScript("myScript2.js", function () { alert("The script \"myScript2.js\" has been correctly loaded."); });
但是应该考虑@Agamemnus 的评论:onloadFunction
调用时脚本可能没有完全加载。可以使用计时器setTimeout(func, 0)
让事件循环完成添加到文档的脚本。事件循环最终调用计时器后面的函数,此时脚本应该可以使用了。
但是,也许应该考虑返回一个 Promise,而不是提供两个用于异常和成功处理的函数,这将是 ES6 的方式。这也将导致不需要计时器,因为 Promise 由事件循环处理 - 因为在处理 Promise 时,脚本已经由事件循环完成。
实现包括 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!"); });
window.addEventListener('DOMContentLoaded',
function() {
var head = document.getElementsByTagName('HEAD')[0];
var script = document.createElement('script');
script.src = "/Content/index.js";
head.appendChild(script);
});