為什么web應(yīng)用程序需要離線運(yùn)行呢?
老實(shí)講,一般來(lái)說(shuō),桌面電腦的web應(yīng)用程序即使能夠完全離線運(yùn)行也不能帶來(lái)多大的好處,因?yàn)樽烂骐娔X一般都是一直連線的。我特別期待看到的是,移動(dòng)設(shè)備web應(yīng)用程序能夠從離線應(yīng)用程序緩存技術(shù)得到多大的好處。
在許多地方,移動(dòng)電話普及率都在持續(xù)增長(zhǎng)。如果能夠自然地填補(bǔ)網(wǎng)絡(luò)斷線的鴻溝,移動(dòng)設(shè)備瀏覽器中的web應(yīng)用程序?qū)τ脩魜?lái)說(shuō)就更加友好了。
在一些特定場(chǎng)景中,使整個(gè)應(yīng)用程序能夠離線運(yùn)行,意味著我們只需創(chuàng)建一個(gè)跨平臺(tái)的瀏覽器解決方案,而不必創(chuàng)建多個(gè)內(nèi)建應(yīng)用程序。
試想一下,一位銷售員需要隨時(shí)隨地向她的顧客展示商品目錄單。她可以使用任何她想要的電子設(shè)備,她首次瀏覽商品目錄單時(shí)需要連線,之后便能夠隨時(shí)隨地離線瀏覽。
應(yīng)用程序緩存技術(shù)并不只是在離線狀態(tài)下才有用武之地。我們可以將應(yīng)用程序緩存作為一個(gè)超級(jí)緩存,用于本地存儲(chǔ)資源,這樣可以加速應(yīng)用程序啟動(dòng)。服務(wù)器上更新了的資源可以在后臺(tái)線程重新加載,加載完成之后便替換掉本地舊的資源并更新到正在運(yùn)行的應(yīng)用程序上。這種方式非常適用于桌面電腦的重量級(jí)web應(yīng)用程序。
清單文件
要使用應(yīng)用程序緩存,你不需要編寫(xiě)大量代碼。你可以在一個(gè)簡(jiǎn)單的文本文件中定義需要離線使用的資源,這個(gè)文件被稱作清單(manifest)文件。
清單文件格式
一個(gè)簡(jiǎn)單的清單文件具有如下格式:
- CACHE MANIFEST
- # Version 1.0
- CACHE:
- /home/index
- /content/style.css
- /scripts/main.js
- NETWORK:
- /service/status
- FALLBACK:
- /logo.png /logo_offline.png
其中,你必須將CACHE MANIFEST 頭放在清單文件的第一行。
以數(shù)字符號(hào)#開(kāi)頭的行是注釋行。這通常用于顯式地修改清單文件以通知瀏覽器更新緩存。比如,在你更新了一張圖片但沒(méi)有修改圖片的名稱時(shí),這種方式非常有用,因?yàn)闉g覽器并沒(méi)有其他方式可以檢測(cè)到服務(wù)器上的圖片已被更新。
接下來(lái),清單文件包含了以下三節(jié):CACHE,NETWORK以及FALLBACK。在CACHE節(jié)你可以指定需要緩存的資源。需要一直從服務(wù)器下載的資源(即使在斷線的情況下)則在NETWORK中指定。如果有大量的資源需要一直從服務(wù)器下載,你可以在NETWORK節(jié)中使用通配字符(即一個(gè)星號(hào)*)表示。在FALLBACK節(jié)中,你可以指定在離線狀態(tài)下可以使用的備用資源。
清單文件的格式并不特別嚴(yán)格。以上介紹的幾個(gè)部分可以是任意次序的,它們甚至可以在一個(gè)清單文件中多次使用。
在清單文件中你可以使用相對(duì)路徑或者絕對(duì)路徑來(lái)定位資源。如果你使用相對(duì)路徑,則必須以清單文件的位置作為參考來(lái)定位資源。
引用清單文件
要將清單文件綁定到應(yīng)用程序,需要將manifest屬性添加到html標(biāo)簽上。每個(gè)引用清單文件的頁(yè)面自身默認(rèn)會(huì)被緩存。然而,還是建議在清單文件中顯示列出你想要緩存的資源。如果某個(gè)頁(yè)面沒(méi)有在清單文件中被指定,同時(shí)也不曾被在線瀏覽過(guò),則在離線狀態(tài)下無(wú)法訪問(wèn)到這個(gè)頁(yè)面,因?yàn)闉g覽器無(wú)法知道頁(yè)面是否存在于本地緩存中。
- <html manifest="cache.manifest")/>
檢查緩存狀態(tài)
使用應(yīng)用程序緩存API,我們可以檢查應(yīng)用程序緩存的狀態(tài)。使用window.applicationCache這個(gè)屬性可以查詢當(dāng)前緩存的狀態(tài)。該狀態(tài)屬性的值是一個(gè)介于0至5之間的數(shù)字,每個(gè)數(shù)字對(duì)應(yīng)一個(gè)特定的緩存狀態(tài)。
你可以使用setInterval函數(shù)來(lái)快速顯示狀態(tài)變化。
- setInterval(function () {
- console.log(window.applicationCache.status)
- }, 500);
事件處理
除了檢查緩存狀態(tài),我們還可以處理特定事件。
緩存替換
當(dāng)新緩存下載完成之后,它并不會(huì)立即替換掉舊的緩存,而是直到我們通知應(yīng)用程序使用新緩存時(shí)它才進(jìn)行替換。我們可以通過(guò)處理updateready事件,使用swapCache將舊緩存替換為新緩存。更新的資源要在刷新頁(yè)面后才能見(jiàn)到。
- window.applicationCache.onupdateready = function(){
- window.applicationCache.swapCache();
- });
怎樣讓用戶知道你的應(yīng)用程序可以離線運(yùn)行呢?
據(jù)我所知,沒(méi)有哪種瀏覽器會(huì)通知用戶當(dāng)前應(yīng)用程序是能離線運(yùn)行的。不過(guò),我們可以自己通知用戶:通過(guò)監(jiān)聽(tīng)?wèi)?yīng)用程序緩存的特定事件,當(dāng)應(yīng)用程序已經(jīng)可以離線工作時(shí)通知用戶。我們甚至可以將應(yīng)用程序緩存生命周期的每個(gè)階段都通知用戶。
應(yīng)用程序緩存相關(guān)事件的處理是直截了當(dāng)?shù)摹F渲幸粋€(gè)特別有用的事件是progress事件。每當(dāng)一個(gè)資源下載完畢時(shí)這個(gè)事件被觸發(fā),其包含三個(gè)非常有用的屬性,我們可以用這三個(gè)屬性來(lái)顯示下載進(jìn)度:lengthComputable、loaded以及total。首先,我們需檢查lengthComputable屬性來(lái)判斷l(xiāng)oaded和total屬性是否可用,接著我們使用loaded和total屬性計(jì)算出資源下載的百分比進(jìn)度。
- window.applicationCache.onchecking = function (e) {
- updateCacheStatus('Checking for a new version of the application.');
- };
- window.applicationCache.ondownloading = function (e) {
- updateCacheStatus('Downloading a new offline version of the application');
- };
- window.applicationCache.oncached = function (e) {
- updateCacheStatus('The application is available offline.');
- };
- window.applicationCache.onerror = function (e) {
- updateCacheStatus('Something went wrong while updating the offline version of the application. It will not be available offline.');
- };
- window.applicationCache.onupdateready = function (e) {
- window.applicationCache.swapCache();
- updateCacheStatus('The application was updated. Refresh for the changes to take place.');
- };
- window.applicationCache.onnoupdate = function (e) {
- updateCacheStatus('The application is also available offline.');
- };
- window.applicationCache.onobsolete = function (e) {
- updateCacheStatus('The application cannot be updated, no manifest file was found.');
- };
- window.applicationCache.onprogress = function (e) {
- var message = 'Downloading offline resources.. ';
- if (e.lengthComputable) {
- updateCacheStatus(message + Math.round(e.loaded / e.total * 100) + '%');
- } else {
- updateCacheStatus(message);
- };
- };
怎樣檢測(cè)瀏覽器是處于在線狀態(tài)還是離線狀態(tài)呢?
你需要知道瀏覽器是在線的還是離線的有以下幾個(gè)原因:也許是因?yàn)槟阆胪ㄖ脩羝湔陔x線工作,也許是因?yàn)槟阆朐诰W(wǎng)絡(luò)斷開(kāi)時(shí)禁用應(yīng)用程序的某些功能,還或許是因?yàn)槟阆胪ㄟ^(guò)本地存儲(chǔ)(local storage)技術(shù)以支持離線用戶輸入,然后在上線時(shí)將用戶輸入的文本同步到服務(wù)器。要實(shí)現(xiàn)這些需求,你可以通過(guò)自造基礎(chǔ)架構(gòu),也可以通過(guò)使用開(kāi)源項(xiàng)目或第三方項(xiàng)目。
檢測(cè)在線狀態(tài)
從原理上講檢測(cè)在線狀態(tài)應(yīng)該是非常簡(jiǎn)單的,比如在標(biāo)準(zhǔn)狀況下,你使用navigator單件的onLine屬性就可以檢測(cè)出當(dāng)前瀏覽器是否在線。
- console.log(navigator.onLine)
但事實(shí)上并非如此簡(jiǎn)單,因?yàn)楦鞣N瀏覽器對(duì)在線和離線的定義不盡相同。比如,舊版本的火狐瀏覽器只當(dāng)用戶顯示地進(jìn)行在線和離線狀態(tài)切換時(shí)才更新onLine屬性的值,而忽略了實(shí)際的網(wǎng)絡(luò)狀況。拋開(kāi)實(shí)現(xiàn)上的不一致,檢測(cè)網(wǎng)絡(luò)連接狀況本身就不是一件微不足道的事情。比如,假設(shè)你的電腦是連接上了的,但是你的路由器出問(wèn)題了,這時(shí)應(yīng)該顯示什么狀態(tài)呢?
一種流行的hack方法是檢查每個(gè)AJAX請(qǐng)求的狀態(tài)碼,然后當(dāng)狀態(tài)碼為不成功時(shí)則回退到離線機(jī)制。
事件處理
如果你想在瀏覽器改變連線狀態(tài)時(shí)做一些事情,你可以通過(guò)處理offline和online事件來(lái)實(shí)現(xiàn)。但是請(qǐng)注意,和檢查onLine屬性一樣,使用這兩個(gè)事件也有類似問(wèn)題。
- window.addEventListener('offline', function(e) {
- console.log('offline');
- }, false);
- window.addEventListener('online', function(e) {
- alert('online');
- }, false);
瀏覽器支持
除了Internet Explorer,所有主流現(xiàn)代瀏覽器都支持離線web應(yīng)用程序。Internet Explorer 10也實(shí)現(xiàn)了相關(guān)規(guī)范,只是目前它還未發(fā)布。在caniuse.com上可以查看到每種瀏覽器及其版本對(duì)這一規(guī)范的支持情況。
對(duì)于大部分實(shí)現(xiàn),各主流瀏覽器基本上是相一致的。但在實(shí)現(xiàn)存儲(chǔ)限額以及對(duì)限額的管理(這兩項(xiàng)沒(méi)有定義在規(guī)范中)上,各瀏覽器差異比較大。在測(cè)試你的web應(yīng)用程序時(shí)應(yīng)該考慮這個(gè)問(wèn)題,移動(dòng)設(shè)備中的瀏覽器在緩存大小上可是斤斤計(jì)較的。
使用ASP.NET MVC生成和提供清單文件
生成清單文件
利用ASP.NET MVC創(chuàng)建和提供清單文件有幾種方式。最簡(jiǎn)單地方式就是讓ASP.NET MVC提供靜態(tài)文本文件。然而,如果我們想要使用內(nèi)建的ASP.NET MVC特性來(lái)解析路由,或者想編寫(xiě)代碼來(lái)動(dòng)態(tài)操控清單文件,我們最好使用自定義的action result。
我把這個(gè)自定義的action result命名為ManifestResult,它繼承自MVC框架中的FileResult類。提供清單文件服務(wù)時(shí)應(yīng)該使用'text/cache-manifest' MIME類型,我把這個(gè)字符串傳遞給了父類的構(gòu)造函數(shù)。
- public class ManifestResult : FileResult
- {
- public ManifestResult(string version)
- : base("text/cache-manifest") { }
- }
ManifestResult類具有四個(gè)屬性,其中三個(gè)屬性對(duì)應(yīng)清單文件的三個(gè)節(jié),另外一個(gè)屬性對(duì)應(yīng)版本號(hào)。表示CACHE節(jié)和NETWORK節(jié)的兩個(gè)屬性僅僅是字符串枚舉,而表示FALLBACK節(jié)的屬性是字典類型的,用于將資源映射到FALLBACK指定的資源。
- public class ManifestResult : FileResult
- {
- public ManifestResult(string version)
- : base("text/cache-manifest")
- {
- Version = version;
- CacheResources = new List<string>();
- NetworkResources = new List<string>();
- FallbackResources = new Dictionary<string, string>();
- }
- public string Version { get; set; }
- public IEnumerable<string> CacheResources { get; set; }
- public IEnumerable<string> NetworkResources { get; set; }
- public Dictionary<string, string> FallbackResources { get; set; }
- }
要將格式化的清單文件輸出到響應(yīng)流,需要重寫(xiě)WriteFile方法。
- protected override void WriteFile(HttpResponseBase response)
- {
- WriteManifestHeader(response);
- WriteCacheResources(response);
- WriteNetwork(response);
- WriteFallback(response);
- }
- private void WriteManifestHeader(HttpResponseBase response)
- {
- response.Output.WriteLine("CACHE MANIFEST");
- response.Output.WriteLine("#V" + Version ?? string.Empty);
- }
- private void WriteCacheResources(HttpResponseBase response)
- {
- response.Output.WriteLine("CACHE:");
- foreach (var cacheResource in CacheResources)
- response.Output.WriteLine(cacheResource);
- }
- private void WriteNetwork(HttpResponseBase response)
- {
- response.Output.WriteLine();
- response.Output.WriteLine("NETWORK:");
- foreach (var networkResource in NetworkResources)
- response.Output.WriteLine(networkResource);
- }
- private void WriteFallback(HttpResponseBase response)
- {
- response.Output.WriteLine();
- response.Output.WriteLine("FALLBACK:");
- foreach (var fallbackResource in FallbackResources)
- response.Output.WriteLine(fallbackResource.Key + " " + fallbackResource.Value);
- }
提供清單文件服務(wù)
為了提供清單文件服務(wù),我們要將相應(yīng)的action添加到相應(yīng)的控制器類中,以生成和返回清單文件的動(dòng)作結(jié)果(action result)。在該action中,我們利用MVC的UrlHelper對(duì)象來(lái)正確地解析路由。
- public ActionResult Manifest()
- {
- var manifestResult = new ManifestResult("1.0")
- {
- CacheResources = new List<string>()
- {
- Url.Action("Index", "Home"),
- "/content/style.css",
- "/scripts/main.js"
- },
- NetworkResources = new string[] { Url.Action("Status", "Service")},
- FallbackResources = { { "/logo.png", "/logo_offline.png" } }
- };
- return manifestResult;
- }
為清單文件設(shè)置路由
我們應(yīng)該為清單文件設(shè)置特定的路由。大多數(shù)瀏覽器對(duì)清單文件的位置沒(méi)有嚴(yán)格的規(guī)定,而最可靠的跨瀏覽器方式是將清單文件放在根目錄,并將其命名為cache.manifest。在應(yīng)用程序啟動(dòng)時(shí),下面的代碼將這個(gè)新的“cache.manifest”路由添加到路由表中。
- routes.MapRoute("cache.manifest", "cache.manifest", new { controller = "Resources", action = "Manifest" });
結(jié)論
離線web應(yīng)用程序是正處于不斷發(fā)展中的HTML規(guī)范的重要內(nèi)容之一。根據(jù)實(shí)際用例,你可能僅僅是利用這個(gè)特性來(lái)緩存資源或者讓web應(yīng)用程序完全離線運(yùn)行。這個(gè)特性的中心就是清單文件。清單文件的格式和要求一點(diǎn)也不復(fù)雜,使用ASP.NET MVC或其他服務(wù)端技術(shù)可以直截了當(dāng)?shù)厣珊吞峁┣鍐挝募?wù)。編寫(xiě)好清單文件之后,使用應(yīng)用程序緩存API就可以很容易地進(jìn)行緩存更新。你也可以使用這組API來(lái)查詢緩存狀態(tài)和處理應(yīng)用程序緩存的特定事件。想知道瀏覽器處于在線狀態(tài)還是離線狀態(tài),你可以通過(guò)檢查navigator對(duì)象的onLine屬性,或者處理特定的在線和離線事件來(lái)判斷。
【網(wǎng)站聲明】本站除付費(fèi)源碼經(jīng)過(guò)測(cè)試外,其他素材未做測(cè)試,不保證完整性,網(wǎng)站上部分源碼僅限學(xué)習(xí)交流,請(qǐng)勿用于商業(yè)用途。如損害你的權(quán)益請(qǐng)聯(lián)系客服QQ:2655101040 給予處理,謝謝支持。