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庫。
圖2 AspNet.ScriptManager.jQuery包
ASP.NET Web Optimization包
如果我們使用的是ASP.NET MVC程序,同樣我們可以通過更新ASP.NET Web Optimization包來管理CDN配置信息。
圖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的地址就可以加載成功了。