引用CDN内容的方法总结

1.1.1 摘要

CDN相信大家都听说过,甚至使用过相关的技术,也许有些人会回答“没有听说过和使用过该技术”,真的是这样吗?
CDN的全称是Content Delivery Network,即内容分发网络。其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络"边缘",使用户可以就近取得所需的内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
看完上面一大串的定义,我们可以把CDN简单的描述为:内容分发,解决网络拥挤和提供网站相应速度。
其实,CDN并不神秘甚至我们在日常开发过程中常常使用到该技术,例如:引用网络脚本库(如:jQuery)和网络图片资源等。
我们经常发现许多网站都从Google的CDN中引用相应的Javascript库,但很多网站都没有考虑如果CDN内容加载失败的情况,我们并不是说Google的CDN很脆弱,只是不怕万一只怕一万。
在接下来的博文中,我们将想大家介绍防范CDN内容加载的方法。

目录

  • 基本方法
  • HTML5 Boilerplate中的解决方法
  • Javascript加载器
  • ASP.NET Web Form 4.5
  • AspNet.ScriptManager.jQuery包
  • ASP.NET Web Optimization包

1.1.2 正文

基本方法
检查CDN内容是否加载成功的基本的方法,我们可以在脚本代码后添加代码判断该类型或变量是否存在,如果不存证明CDN加载失败,那么我们的程序就应该加载本地脚本,下面我们以加载jQuery库为例,具体实现如下:

<!-- Adds google cdn reference -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>

<!-- Cdn fail refers to local library -->
<script type="text/javascript">
    if (typeof jQuery == 'undefined') {
        document.write(unescape("%3Cscript src='js/jquery-2.0.0.min.js' type='text/javascript'%3E%3C/script%3E"));
    }
</script>

上面,我们引用了 Google CDN 的jQuery库,接着我们在脚本代码后添加了一个 if 语句来判断 jQuery 库是否加载成功,如果没有加载成功我们动态加载本地 jQuery 库。
其中,我们在 document.write 方法中直接使用了URL编码,把“<”编码为“%3C”,接着我们再使用 unescape() 方法把字符串还原过来。


图1字符编码

上图,我们通过 unescape() 方法把字符串转换回来,我们可以看到输出是一个正常的脚本引用代码。
现在,我们有一个疑问就是“为什么不使用常规字符,而是要使用字符编码呢?”,其实这是有原因的,这意味着我们代码将可以在XML、XHTML或HTML中正常运行,而无需把代码包含在 CDATA 中(具体请参考这里)。

HTML5 Boilerplate中的解决方法
接下来,我们将介绍使用“协议省略”的引用地址和简化本地加载代码,在介绍之前,首先让我们了解使用“协议省略”的引用地址的优点。
我们知道使用安全的引用地址对于确保信息安全是无可厚非的,但过度地使用SSL缓存静态资源例如:jQuery库,也会导致加载的性能降低;出于同样的原因浏览器需要对这些资源进行加密,而且大多数浏览器默认不缓存通过SSL方式获取的文件。
更糟糕的是,即使用户已经有一个本地缓存副本在磁盘上,但通过HTTP请求从Google的CDN获取jQuery文件,不能被同一请求不同协议HTTPS使用,也就是说,对于同一请求不同协议要保存两个缓存文件。
Http请求的URL地址:
http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js
下面是使用Https请求的URL地址:
https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js
当我们在常规的Http页面中通过Https方式来引用Google CDN的jQuery库将导致缓存性能下降,我们应该尽量避免在Http页面中引用不必要的Https内容。
在RFC3986中的第4.2节规定合法的URL省略了协议(Http或Http)还是合法的,当一个URL的协议被省略时,浏览器将使用基本的文档的协议,通过这种方式,我们可以更加灵活地指定URL地址。所以我们可以通过以下的方式引用jQuery库。

<!-- Adds protocol-less google cdn reference -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>

上面的代码看起来奇怪,但“协议省略”的网址是引用第三方内容的最好的方式,它可以通过Http或Https引用。
当页面加载时,对于非加密请求脚本会通过Http方式引用并且缓存起来,以此同时对于加密请求脚本会根据“协议省略”方式使用Https引用内容,所以使用“协议省略”的URL允许单个脚本更灵活地引用内容。
接下来,我们继续简化加载本地脚本的代码,当jQuery成功加载到页面中,它会创建一个全局的jQuery变量,我们可以通过window的jQuery属性(window.jQuery)访问该变量的值,如果jQuery没有加载成功,那么window.jQuery就是未定义的。
所以,我们可以把代码简化成如下:

<!-- Adds protocol-less google cdn reference -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<!--<script src="//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>

上面,我们使用了“||”运算符判断window.jQuery是否为未定义类型,如果 window.jQuery 未定义执行后面的代码加载本地jQuery脚本,HTML5 Boilerplate 就使用以上的方法处理CDN内容加载失败的情况。

Javascript加载器
有些人使用JavaScript加载器如:yepnope,它是一个能够根据输入条件来选择性异步加载资源文件的js脚本,可以在页面上仅加载用户需要的js或css,下面我们以加载jQuery为例子介绍yepnope的使用,具体实现如下:

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

上面,我们使用加载器 yepnope 把 jQuery 加载到页面中,接下来,我们介绍使用 RequireJS 加载 jQuery,具体实现如下:

// Uses requestJS to add cdn reference.    
requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: [
            '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js',
            //If the CDN location fails, load from this location
            'js/jquery-2.0.0.min'
        ]
    }
});
//Later
require(['jquery'], function($) {
    // Your code here.
});

我们可以RequireJS加载器中使用“协议省略”的URL地址,这样我们可以避免前面提到的加密协议和非加密协议加密和缓存问题了。
虽然,JS加载器也可以解决CDN内容加载失败的问题,但仅仅为了防止CDN内容加载失败问题而引入yepnope 或 RequireJS是没有必要的。

ASP.NET Web Form 4.5
对于每个ASP.NET Web窗体开发人员,我们需要了解在ASP.NET 4.5增加的新特性,其中就包含了处理CDN内容加载失败转移加载本地内容的特性。
首先,我们在页面中添加ScriptManager控件,然后设置该控制的EnableCdn属性为“True”,接着我们添加需要加载的内容名称,下面以加载jQuery为例:

<!-- Set up cdn -->       
<asp:ScriptManager runat="server" EnableCdn="true">
    <Scripts>
        <asp:ScriptReference Name="jquery" />
    </Scripts>
</asp:ScriptManager>

上面,我们通过 ScriptManager 控件指定加载 jQuery库,但这里我们有一个疑问首先是我们没有指定加载 jQuery库的URL地址,第二在加载失败后,没有指定本地 jQuery库路径。
现在,我们通过运行页面代码查看 ScriptManager 生成的脚步代码,具体代码如下:

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.2.js" type="text/javascript"></script>
<script type="text/javascript">
    //<![CDATA[
    (window.jQuery) || document.write('<script type="text/javascript" src="Scripts/jquery-1.8.2.js"><\/script>'); //]]>
</script>

通过上面的代码,我们发现ScriptManager默认加载jQuery库的URL是 http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.2.js 而且一旦加载失败它自动到Scripts文件中加载本地脚步。
虽然这种方式简单而且省心,但它默认是到微软的CDN中加载内容的,如果我们想使用Google的CDN呢?难道微软打算CDN也搞垄断吗?还有我们想使用jQuery 2.0.0库应该如何加载呢?
其实,我们可以通过自定义 ScriptResourceMapping 方式来指定加载的 CDN和 jQuery库,我们要在 Global.asax 文件中添加以下代码:

var mapping = ScriptManager.ScriptResourceMapping;
// Map jquery definition to the Google CDN
mapping.AddDefinition("jquery", new ScriptResourceDefinition
{
    Path = "~/Scripts/jquery-2.0.0.min.js",
    DebugPath = "~/Scripts/jquery-2.0.0.js",
    CdnPath = "http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js",
    CdnDebugPath = "https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.js",
    CdnSupportsSecureConnection = true,
    LoadSuccessExpression = "window.jQuery"
});

上面,我们定义了加载 Google的 CDN和 jQuery 2.0.0版本,接下来我们重新运行页面代码查看 ScriptManager生成的脚步代码是否对应我们的设置。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
    //<![CDATA[
    (window.jQuery) || document.write('<script type="text/javascript" src="Scripts/jquery-2.0.0.js"><\/script>'); //]]>
</script>

现在,我们看到使用的是Google的CDN,并且加载jQuery的版本为2.0.0的。

AspNet.ScriptManager.jQuery包
在ASP.NET4.5中,如果我们想使用jQuery 2.0.0或更新的版本,其实我们可以通过使用AspNet.ScriptManager.jQuery包来管理引用库的更新,其中它也包括配置CDN信息的功能,如果我们要获取最新的更新,首先我们选择工具->扩展工具管理器->AspNet.ScriptManager.jQuery,然后更新包就可以获取最新的jQuery库。

ASP.NET Web Optimization包
如果我们使用的是ASP.NET MVC程序,同样我们可以通过更新ASP.NET Web Optimization包来管理CDN配置信息。


接下来,我们在的 BundleConfig 中指定我们的 CdnPath 的 URL 地址,打开 App_Start\BundleConfig.cs,我们可以看到里面有一个 RegisterBundles() 方法,下面是 RegisterBundles() 方法的一部分代码:

/// <summary>
/// Sets up the jquery libs load path.
/// </summary>
/// <param name="bundles"></param>
public static void RegisterBundles(BundleCollection bundles)
{
    bundles.UseCdn = true;
    BundleTable.EnableOptimizations = true; //force optimization while debugging

    var jquery = new ScriptBundle("~/bundles/jquery", "//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js").Include(
            "~/Scripts/jquery-{version}.js");
    jquery.CdnFallbackExpression = "window.jQuery";
    bundles.Add(jquery);
    //...
}

1.1.3 总结

在本文中,我们向大家介绍了一些 CDN 内容引用失败的处理机制,就处理方式来说,我觉得 HTML5 Boilerplate 的解决方法不但简洁,而且不用引入第三方库。
同时我们也介绍了使用“协议省略”的 CDN 的优点,但有一点我们要注意的,如果我们在本地运行使用“协议省略”的 URL 可能不会得到如预期运行效果,由于我们在本地运行页面“协议省略”使用的基础协议是 file,而不是 http 或 https,所以一般来说我们在本地直接运行页面是无法正确获取到 CDN 的内容,我们可以把页面放到本地Web服务器中运行,如 Apache 或IIS中,然后运行 http://localhost的地址就可以加载成功了。
参考
http://www.hanselman.com/blog/CDNsFailButYourScriptsDontHaveToFallbackFromCDNToLocalJQuery.aspx
http://encosia.com/cripple-the-google-cdns-caching-with-a-single-character/
https://developers.google.com/speed/libraries/devguide

原著:JK_Rush 原文链接:引用CDN内容的方法总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • <a name='html'>HTML</a> Doctype作用?标准模式与兼容模式各有什么区别? (1)、<...
    clark124阅读 3,474评论 1 19
  • @转自GitHub 介绍js的基本数据类型。Undefined、Null、Boolean、Number、Strin...
    YT_Zou阅读 1,153评论 0 0
  • 在线阅读 http://interview.poetries.top[http://interview.poetr...
    程序员poetry阅读 114,359评论 24 450
  • 滑块视图容器常用属性: swiper.js添加代码: swiper.wxss添加代码: 来源:http://bbs...
    小码哥教育520it阅读 9,451评论 1 1
  • 我喜欢夜。 夜沉寂而深邃,让人在静静的孤独中不再孤单。那时,所面对的,是一个真实的自己,脆弱得总要流泪,流泪后更为...
    泠风思语阅读 151评论 0 1