CDN內容的引用方法總結

五柳-先生發表於2015-06-24

1.1.1 摘要

CDN相信大家都聽說過,甚至使用過相關的技術,也許有些人會回答“沒有聽說過和使用過該技術”,真的是這樣嗎?

CDN的全稱是Content Delivery Network,即內容分發網路。其目的是通過在現有的Internet中增加一層新的網路架構,將網站的內容釋出到最接近使用者的網路"邊緣",使使用者可以就近取得所需的內容,解決 Internet網路擁擠的狀況,提高使用者訪問網站的響應速度。

看完上面一大串的定義,我們可以把CDN簡單的描述為:內容分發,解決網路擁擠和提供網站相應速度。

其實,CDN並不神祕甚至我們在日常開發過程中常常使用到該技術,例如:引用網路指令碼庫(如:jQuery)和網路圖片資源等。

我們經常發現許多網站都從Google的CDN中引用相應的Javascript庫,但很多網站都沒有考慮如果CDN內容載入失敗的情況,我們並不是說Google的CDN很脆弱,只是不怕萬一只怕一萬。

在接下來的博文中,我們將想大家介紹防範CDN內容載入的方法。

目錄

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()方法把字串還原過來。

cnd0

圖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庫。

 cdn1

圖2 AspNet.ScriptManager.jQuery包

ASP.NET Web Optimization包

如果我們使用的是ASP.NET MVC程式,同樣我們可以通過更新ASP.NET Web Optimization包來管理CDN配置資訊。

cdn2

圖3 ASP.NET Web Optimization包

接下來,我們在的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的地址就可以載入成功了。

參考

相關文章