使用 Google 托管 jQuery 的最佳方式,但回退到我在 Google 上的托管库失败
-
06-07-2019 - |
题
尝试加载的好方法是什么 Google 托管的 jQuery (或其他 Google 托管的库),但如果 Google 尝试失败,加载我的 jQuery 副本?
我并不是说谷歌很脆弱。在某些情况下,谷歌副本会被屏蔽(例如在伊朗)。
我会设置一个计时器并检查 jQuery 对象吗?
两个副本都通过会有什么危险?
并不是真正寻找“只需使用Google One”或“只使用自己的”之类的答案。我了解那些论点。我还了解到,用户很可能缓存了 Google 版本。我正在考虑云的总体后备方案。
编辑:这部分添加了...
由于 Google 建议使用 google.load 加载 ajax 库,并且完成后执行回调,我想知道这是否是序列化此问题的关键。
我知道这听起来有点疯狂。我只是想弄清楚是否可以以可靠的方式完成。
更新:jQuery 现在托管在 Microsoft 的 CDN 上。
解决方案
你可以这样做:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="/path/to/your/jquery"><\/script>');
</script>
这应该在你的页面中<head>
,并且任何jQuery ready事件处理程序都应该在<body>
中以避免错误(尽管它不是万无一失的!)。
不使用Google托管的jQuery的另一个原因是,在某些国家/地区,Google的域名被禁止。
其他提示
到目前为止,这是最简单,最干净的方法:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="path/to/your/jquery"><\/script>')</script>
这似乎对我有用:
<html>
<head>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// has the google object loaded?
if (window.google && window.google.load) {
google.load("jquery", "1.3.2");
} else {
document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
window.onload = function() {
$('#test').css({'border':'2px solid #f00'});
};
</script>
</head>
<body>
<p id="test">hello jQuery</p>
</body>
</html>
它的工作方式是使用google
调用 http://www.google.com/的对象jsapi 加载到window
对象上。如果该对象不存在,我们假设对Google的访问失败。如果是这种情况,我们使用document.write
加载本地副本。 (我在这种情况下使用自己的服务器,请使用自己的服务器进行测试)。
我还测试window.google.load
的存在 - 我也可以进行typeof
检查以确定事物是对象或函数是否合适。但我认为这样就可以了。
这只是加载逻辑,因为代码突出显示似乎失败了,因为我发布了我正在测试的整个HTML页面:
if (window.google && window.google.load) {
google.load("jquery", "1.3.2");
} else {
document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
虽然我必须说,我不确定如果这对您的网站访问者来说是一个问题,您应该摆弄 Google AJAX Libraries API 。
有趣的事实:我最初尝试在各种版本中使用try..catch块,但找不到像这样干净的组合。我有兴趣看到这个想法的其他实现,纯粹作为练习。
如果您的网站上嵌入了modernizr.js,您可以使用内置的yepnope.js异步加载脚本 - 其他jQuery(带后备)。
Modernizr.load([{
load : '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'
},{
test : window.jQuery,
nope : 'path/to/local/jquery-1.7.2.min.js',
both : ['myscript.js', 'another-script.js'],
complete : function () {
MyApp.init();
}
}]);
这会从Google-cdn加载jQuery。然后检查,如果jQuery成功加载。如果不是(<!> quot; nope <!> quot;),则加载本地版本。你的个人脚本也被加载 - <!>;两个<!>表示负载过程与测试结果无关。
当所有加载过程完成后,在“MyApp.init”的情况下执行一个函数。
我个人更喜欢这种异步脚本加载方式。因为我依赖于现代化建设网站时提供的功能测试,所以无论如何我都将它嵌入到网站上。所以实际上没有开销。
这里有一些很好的解决方案,但我想在本地文件方面更进一步。
在 Google 确实失败的情况下,它应该加载本地源,但服务器上的物理文件不一定是最佳选择。我提出这个问题是因为我目前正在实现相同的解决方案,只是我想回退到由数据源生成的本地文件。
我这样做的原因是,在跟踪我从 Google 和其他网站加载的内容时,我想有一些想法。我在本地服务器上有什么。如果我想更改版本,我需要使本地副本与我尝试从 Google 加载的内容保持同步。在有许多开发人员的环境中,我认为最好的方法是自动化此过程,这样人们所要做的就是更改配置文件中的版本号。
这是我提出的解决方案,理论上应该有效:
- 在应用程序配置文件中,我将存储 3 件事:库的绝对 URL、JavaScript API 的 URL 以及版本号
- 编写一个类,获取库本身的文件内容(从应用程序配置获取 URL),将其与名称和版本号一起存储在我的数据源中
- 编写一个处理程序,将本地文件从数据库中取出并缓存该文件,直到版本号发生变化。
- 如果它确实发生变化(在我的应用程序配置中),我的类将根据版本号提取文件内容,将其保存为我的数据源中的新记录,然后处理程序将启动并提供新版本。
理论上,如果我的代码编写正确,我需要做的就是更改我的应用程序配置中的版本号,然后中提琴!您有一个自动化的后备解决方案,并且您不必在服务器上维护物理文件。
大家觉得怎么样?也许这有些过头了,但它可能是维护 AJAX 库的一种优雅方法。
橡子
if (typeof jQuery == 'undefined') {
// or if ( ! window.jQuery)
// or if ( ! 'jQuery' in window)
// or if ( ! window.hasOwnProperty('jQuery'))
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/libs/jquery.js';
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
尝试从CDN中加入Google副本后。
在HTML5中,您无需设置type
属性。
你也可以使用......
window.jQuery || document.write('<script src="/libs/jquery.js"><\/script>');
您可能希望将本地文件用作最后的手段。
到目前为止,jQuery自己的CDN似乎不支持https。如果它确实你那么可能想先从那里加载。
所以这是顺序: Google CDN = <!> gt; Microsoft CDN = <!> gt; 你的本地副本。
<!-- load jQuery from Google's CDN -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<!-- fallback to Microsoft's Ajax CDN -->
<script> window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js">\x3C/script>')</script>
<!-- fallback to local file -->
<script> window.jQuery || document.write('<script src="Assets/jquery-1.8.3.min.js">\x3C/script>')</script>
有条件地加载最新/旧版jQuery版本和后备:
<!--[if lt IE 9]>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/public/vendor/jquery-legacy/dist/jquery.min.js">\x3C/script>')</script>
<![endif]-->
<!--[if gte IE 9]><!-->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/public/vendor/jquery/dist/jquery.min.js">\x3C/script>')</script>
<!--<![endif]-->
- 步骤1:jQuery 加载失败?(查看
jQuery
多变的)
- 第2步:动态导入(备份)javascript文件
由于谷歌的禁止问题,我更喜欢使用微软的cdn http://www.asp.net/ajaxlibrary/cdn.ashx
对于那些使用ASP.NET MVC 5的人,在BundleConfig.cs中添加此代码以启用CDN for jquery:
bundles.UseCdn = true;
Bundle jqueryBundle = new ScriptBundle("~/bundles/jquery", "//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js").Include("~/Scripts/jquery-{version}.js");
jqueryBundle.CdnFallbackExpression = "window.jQuery";
bundles.Add(jqueryBundle);
<强>更新强>结果 这个答案结果证明是错误的。请参阅评论以获得真实的解释。
大部分问题都已得到解答,但最后一部分是:
两份副本的危险是什么?
没有。你浪费带宽,可能会增加几毫秒下载第二个无用的副本,但如果它们都通过,那么实际上并没有什么危害。当然,您应该使用上述技术来避免这种情况。
我创建了一个Gist,它应该动态加载jQuery(如果它尚未加载),如果源失败,它将继续进行回退(从许多答案中拼接在一起): https://gist.github.com/tigerhawkvok/9673154
请注意我计划保持Gist更新但不是这个答案,因为它的价值!
/* See https://gist.github.com/tigerhawkvok/9673154 for the latest version */
function cascadeJQLoad(i) { // Use alternate CDNs where appropriate to load jQuery
if (typeof(i) != "number") i = 0;
// the actual paths to your jQuery CDNs
var jq_paths = [
"ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js",
"ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.0.min.js"
];
// Paths to your libraries that require jQuery
var dependent_libraries = [
"js/c.js"
];
if (window.jQuery === undefined && i < jq_paths.length) {
i++;
loadJQ(jq_paths[i], i, dependent_libraries);
}
if (window.jQuery === undefined && i == jq_paths.length) {
// jQuery failed to load
// Insert your handler here
}
}
/***
* You shouldn't have to modify anything below here
***/
function loadJQ(jq_path, i, libs) { //load jQuery if it isn't already
if (typeof(jq_path) == "undefined") return false;
if (typeof(i) != "number") i = 1;
var loadNextJQ = function() {
var src = 'https:' == location.protocol ? 'https' : 'http';
var script_url = src + '://' + jq_path;
loadJS(script_url, function() {
if (window.jQuery === undefined) cascadeJQLoad(i);
});
}
window.onload = function() {
if (window.jQuery === undefined) loadNextJQ();
else {
// Load libraries that rely on jQuery
if (typeof(libs) == "object") {
$.each(libs, function() {
loadJS(this.toString());
});
}
}
}
if (i > 0) loadNextJQ();
}
function loadJS(src, callback) {
var s = document.createElement('script');
s.src = src;
s.async = true;
s.onreadystatechange = s.onload = function() {
var state = s.readyState;
try {
if (!callback.done && (!state || /loaded|complete/.test(state))) {
callback.done = true;
callback();
}
} catch (e) {
// do nothing, no callback function passed
}
};
s.onerror = function() {
try {
if (!callback.done) {
callback.done = true;
callback();
}
} catch (e) {
// do nothing, no callback function passed
}
}
document.getElementsByTagName('head')[0].appendChild(s);
}
/*
* The part that actually calls above
*/
if (window.readyState) { //older microsoft browsers
window.onreadystatechange = function() {
if (this.readyState == 'complete' || this.readyState == 'loaded') {
cascadeJQLoad();
}
}
} else { //modern browsers
cascadeJQLoad();
}
Google 托管 jQuery
- 如果您关心旧版浏览器,主要是 IE9 之前的 IE 版本,这是兼容最广泛的 jQuery 版本
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
- 如果你不关心 oldIE,这个更小更快:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
备份/后备计划!
- 无论哪种方式,您都应该使用回退到本地,以防 Google CDN 失败(不太可能)或在您的用户访问您的网站的位置(可能性稍大一些)被阻止,例如伊朗或有时是中国。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>if (!window.jQuery) { document.write('<script src="/path/to/your/jquery"><\/script>'); }
</script>
参考: http://websitespeedoptimizations.com/ContentDeliveryNetworkPost.aspx
我认为应该逃避最后的<!> lt;到字符串中的\ x3C。当浏览器看到时,它认为这是脚本块的结尾(因为HTML解析器不知道JavaScript,它无法区分刚出现在字符串中的东西,以及实际上意味着结束脚本的东西元件)。因此,在HTML页面中出现的字面意思(在最好的情况下)将导致错误,并且(在最坏的情况下)是一个巨大的安全漏洞。
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-2.0.0.min.js">\x3C/script>')</script>
if (typeof jQuery == 'undefined')) { ...
或者
if(!window.jQuery){
如果没有加载cdn版本将不起作用,因为浏览器将运行这个条件并且在它仍然下载需要jQuery的其余javascripts并且它返回错误。解决方案是在这种情况下加载脚本。
<script src="http://WRONGPATH.code.jquery.com/jquery-1.4.2.min.js" type="text/javascript"></script><!-- WRONGPATH for test-->
<script type="text/javascript">
function loadCDN_or_local(){
if(!window.jQuery){//jQuery not loaded, take a local copy of jQuery and then my scripts
var scripts=['local_copy_jquery.js','my_javascripts.js'];
for(var i=0;i<scripts.length;i++){
scri=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
scri.type='text/javascript';
scri.src=scripts[i];
}
}
else{// jQuery loaded can load my scripts
var s=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
s.type='text/javascript';
s.src='my_javascripts.js';
}
}
window.onload=function(){loadCDN_or_local();};
</script>
在ASP.NET中使用Razor语法,此代码提供后备支持并使用虚拟根目录:
@{var jQueryPath = Url.Content("~/Scripts/jquery-1.7.1.min.js");}
<script type="text/javascript">
if (typeof jQuery == 'undefined')
document.write(unescape("%3Cscript src='@jQueryPath' type='text/javascript'%3E%3C/script%3E"));
</script>
或者做一个帮手(帮助器概述):
@helper CdnScript(string script, string cdnPath, string test) {
@Html.Raw("<script src=\"http://ajax.aspnetcdn.com/" + cdnPath + "/" + script + "\" type=\"text/javascript\"></script>" +
"<script type=\"text/javascript\">" + test + " || document.write(unescape(\"%3Cscript src='" + Url.Content("~/Scripts/" + script) + "' type='text/javascript'%3E%3C/script%3E\"));</script>")
}
并像这样使用它:
@CdnScript("jquery-1.7.1.min.js", "ajax/jQuery", "window.jQuery")
@CdnScript("jquery.validate.min.js", "ajax/jquery.validate/1.9", "jQuery.fn.validate")
尽管编写document.write("<script></script>")
似乎更容易进行jQuery退避,但Chrome会在该案例中提供验证错误。所以我更喜欢打破<!> quot; script <!> quot;字。所以它变得像上面那样安全。
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script>if (typeof jQuery === "undefined") {
window.jqFallback = true;
document.write("<scr"+"ipt src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js'></scr"+"ipt>");
} </script>
对于长期问题,最好记录JQuery回退。在上面的代码中,如果第一个CDN不可用,则从另一个CDN加载JQuery。但你可能想知道错误的CDN并永久删除它。 (这种情况非常特殊)同样最好记录后备问题。所以你可以用AJAX发送错误的案例。由于未定义JQuery,因此您应该使用vanilla javascript进行AJAX请求。
<script type="text/javascript">
if (typeof jQuery === 'undefined' || window.jqFallback == true) {
// XMLHttpRequest for IE7+, Firefox, Chrome, Opera, Safari
// ActiveXObject for IE6, IE5
var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
var url = window.jqFallback == true ? "/yourUrl/" : "/yourUrl2/";
xmlhttp.open("POST", url, true);
xmlhttp.send();
}
</script>
无法从外部数据存储加载超出您控制范围的资源是很困难的。寻找缺失的功能完全是错误的,作为避免超时的手段,如本文所述: http://www.tech-101.com/ support / topic / 4499-issues-using-a-cdn /
另一个后备方案取代了 ajax.googleapis.com 和 cdnjs.cloudflare.com:
(function (doc, $)
{
'use strict';
if (typeof $ === 'undefined')
{
var script = doc.querySelector('script[src*="jquery.min.js"]'),
src = script.src.replace('ajax.googleapis.com', 'cdnjs.cloudflare.com');
script.parentNode.removeChild(script);
doc.write('<script src="' + src + '"></script>');
}
})(document, window.jQuery || window.Zepto);
- 您可以通过在字符串中指定来坚持使用 jQuery 版本
- 非常适合无法使用 HTML 片段的资产管理
- 经过野外测试 - 非常适合中国用户
您可以使用如下代码:
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>window.jQuery || document.write('<script type="text/javascript" src="./scripts/jquery.min.js">\x3C/script>')</script>
但也有一些库可以用来设置 一些 脚本可能的后备方案并优化加载过程:
- 篮子.js
- 要求JS
- 耶普诺普
例子:
篮子.js我认为目前最好的变体。将在本地存储中缓存您的脚本,这将加快下次加载的速度。最简单的调用:
basket.require({ url: '/path/to/jquery.js' });
这将返回一个承诺,您可以在错误时执行下一个调用,或在成功时加载依赖项:
basket
.require({ url: '/path/to/jquery.js' })
.then(function () {
// Success
}, function (error) {
// There was an error fetching the script
// Try to load jquery from the next cdn
});
要求JS
requirejs.config({
enforceDefine: true,
paths: {
jquery: [
'//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min',
//If the CDN location fails, load from this location
'js/jquery-2.0.0.min'
]
}
});
//Later
require(['jquery'], function ($) {
});
耶普诺普
yepnope([{
load: 'http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js',
complete: function () {
if (!window.jQuery) {
yepnope('js/jquery-2.0.0.min.js');
}
}
}]);
几乎所有公共CDN都非常可靠。但是,如果您担心被屏蔽的谷歌域名,那么您可以简单地回退到其他公共CDN,如 PageCDN , CDNJS 或 jsDelivr :
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"><\/script>');
</script>
但是,在这种情况下,您可能更愿意使用其他CDN作为首选选项并使用Google CDN回退以避免请求失败和等待时间:
<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"><\/script>');
</script>