使用 Google 托管 jQuery 的最佳方式,但回退到我在 Google 上的托管库失败

StackOverflow https://stackoverflow.com/questions/1014203

尝试加载的好方法是什么 Google 托管的 jQuery (或其他 Google 托管的库),但如果 Google 尝试失败,加载我的 jQuery 副本?

我并不是说谷歌很脆弱。在某些情况下,谷歌副本会被屏蔽(例如在伊朗)。

我会设置一个计时器并检查 jQuery 对象吗?

两个副本都通过会有什么危险?

并不是真正寻找“只需使用Google One”或“只使用自己的”之类的答案。我了解那些论点。我还了解到,用户很可能缓存了 Google 版本。我正在考虑云的总体后备方案。


编辑:这部分添加了...

由于 Google 建议使用 google.load 加载 ajax 库,并且完成后执行回调,我想知道这是否是序列化此问题的关键。

我知道这听起来有点疯狂。我只是想弄清楚是否可以以可靠的方式完成。


更新:jQuery 现在托管在 Microsoft 的 CDN 上。

http://www.asp.net/ajax/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 多变的)

如何在 JavaScript 中检查未定义的变量

  • 第2步:动态导入(备份)javascript文件

如何将一个 JavaScript 文件包含在另一个 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.comcdnjs.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>
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top