第十章 构建离线Web应用
本章,我们将探讨如何创建HTML5离线Web应用。HTML5应用并不需要始终保持网络络连接,而现在,开发人员可以更加灵活地控制缓存资源的加载。
10.1 HTML5离线Web应用
很明显,在Web应用中使用缓存的原因之一是为了支持离线应用。在全球互联的时代,离线应用仍有其实用价值。当无法上网的时候,你会做些什么呢?有人可能会说如今网络无处不在,而且非常稳定,不存没有网络的情况。但事实果真如此吗?下面这些问题,你考虑到了吗?
- 我们乘坐的所有航班都有WI-FI吗?
- 我们的移动网络设备的信号好吗?最后一次遇到无信号是什么时候 ?
- 我们去做讲演时,一定能够上网吗?
越来越多的应用移植到了Web 上,我们倾向于认为用户拥有24小时不间断的网络连接。但事实上,网络连接中断时有发生,例如在乘坐飞机的情况下,可预见的中断时间一次就可能达到好几个小时。
间断性的网络连接一直是网络计算系统致命的弱点。如果应用程序依赖于与远程主机的通信,而这些主机又无法连接时,用户就无法正常使用应用程序了。不过当网络连接正常时, Web应用程序可以保证及时更新,因为用户每次使用,应用程序都会从远程位置更新加载相关数据。
如果应用程序只需要偶尔进行网络通信,那么只要在本地存储了应用资源,无论是否连接网络它都可用。随着完全依赖于浏览器的设备的出现,Web应用程序在不稳定的网络状态下还能够持续工作就变得更加重要。在这方面,不需要持续连接网络的桌面应用程序历来被认为比Web应用程序更有优势。
HTML5的缓存控制机制综合了Web应用和桌面应用两者的优势:基于Web技术构建的Web应用程序,可在浏览器中运行并在线更新,也可在脱机情况下使用。然而,因为目前的Web服务器不为脱机应用程序提供任何默认的缓存行为,所以要想使用这一新的离线应用功能,你必须在应用中明确声明。
HTML5的离线应用缓存使得在无网络连接状态下运行应用程序成为可能。这类应用程序用处很多,比如在起草电子邮件草稿时就无需连接因特网, HTML5中引入了离线应用缓存,有了它Web应用程序就可以在没有网络连接的情况下运行。
应用程序开发人员可以指定HTML5应用程序中,具体哪些资源(HTML、CSS、JavaScript和图像)脱机肘可用。离线应用的适用场景很多,例如:
- 阅读和撰写电子邮件;
- 编辑文档
- 编辑和显示演示文档
- 创建代办事宜列表。
使用离钱存储,避免了加载应用程序时所需的常组网络请求。如果缓存清单(cache manifest)文件是最新的。浏览器就知道自己无需检查其他资源是否最新。大部分应用程序可以非常迅速地从本地应用缓存中加载完成。此外,从缓存中加载资源(而不必用多个HTTP 请求确定资源是否已经更新)可节省带宽,这对于移动Web应用是至关重要的。目前,加载速度慢是Web应用比不上应用的一个地方。缓存则可以解决这一问题。
开发人员可以直接控制应用程序缓存。利用缓存清单文件可将相关资源组织到同一个逻辑应用中。这样一来,Web应用就拥有了本来只属于桌面应用的特性。你可以充分发挥想象力,尝试用一些更巧妙的方式利用这些特性。
缓存清单文件中标识的资源构成了应用缓存(application cache) ,它是浏览器持久性存储资源的地方,通常在硬盘上。有些浏览器向用户提供了查看应用程序缓存中数据的方法。例如,在最新版本的Firefox中,about:cache页面会显示应用程序缓存的详细信息,提供了查看缓存中的每个文件的方法,如图10-1所示。
10.1 HTML5离线Web应用的浏览器支持情况
编写本书时,各浏览器对HTML5离线Web应用的浏览器支持情况见表10-1。从下表中可以看出,许多浏览器已经支持HTML5离钱Web应用。
表10-1 支持HTML5离线应用缓存的浏览器
浏览器 | 描述 |
Chrome | 4.0及以上的版本均支持 |
Firefox | 3.5及以上的版本均支持 |
Internet Explorer | 不支持 |
Opera | 10.6及以上的版本均支持 |
Safari | 4.0及以上的版本均支持 |
由于目前不同的浏览器对于HTML5离线应用的支持程度不同,所以在使用之前最好先测试浏览器的支持情况。
10.2 使用HTML5离线Web应用API
本节,我们将更加细致地探讨如何使用HTML5离线Web应用API。
10.2.1 检查浏览器的支持情况
使用离缉Web应用APl前最好先检查浏览器是否支持它。代码清单10-1演示了检测方法。
代码清单10-1 检查浏览器是否支持离线Web应用API
if(window.applicationCache) { // this browser supports offline applications }
10.2.2 搭建简单的离线应用程序
假设开发人员希望搭建一个包含HTML文档、样式表和JavaScript文件的单页面应用程序,同时要为这个HTML5应用程序添加离线线支持,那么可参考代码清单10-2,在html 元素中加入manifest特性。
代码清单10-2 HTML元素中manifest特性
<!DOCTYPE html> <html manifest="application.manifest"> . . . </html>
修改完HTML文挡,接下来需要提供缓存清单文件,用以指明哪些资源需要存储在缓存中。代码清单10-3是一个缓存清单文件的内容示例。
代码清单10-3是一个缓存清单文件的内容示例
CACHE MANIFEST example.html example.js example.css example.gif
10.2.3 支持离线行为
HTML5引入了一些新的事件,用来让应用程序检测网络是否正常连接。应用程序处于在线状态和离线状态会有不同的行为模式。 是否处于在线状态可以通过检测window.navigator对象的属性来做判断。首先,navigator. online是一个标明浏览器是否处于在线状态的布尔属性。当然,onLine值为true并不能保证Web应用程序在用户的机器上一定能访问到相应的服务器。而当其值为false时,不管浏览器是否真正联网。应用程序都不会尝试进行网络连接。代码清单10-4演示了如何查看页面状态是在钱还是离钱。它甚至可以在 Internet Explorer中使用。
代码清单10-4 检测在线状态
// When the page loads, set the status to online or offline function loadDemo() { if (navigator.onLine) { log("Online"); } else { log("Offline"); } } // Now add event listeners to notify a change in online status window.addEventListener("online", function(e) { log("Online"); }, true); window.addEventListener("offline", function(e) { log("Offline"); }, true);
10.2.4 manifest文件
离钱应用包含一个manifest (清单)文件,此文件中列出了浏览器为离线应用缓存的所有资源。manifest文件的MIME类型是text/cache-manifest。phthon标准库中的SimpleHTTPServer模块对扩展名为.manifest的文件能配以头部信息Content-type: text/cache -manifest. 配置方法是打开PYTHON_HOME/ Lib/mimetypel.py文件并添加一行代码:
'.manifest' : 'text/cache-manifest manifest',
不同的Web服务器都有其独特的配置方法。例如,要配置Apache HTTP服务器,开发人员需要将下面一行代码添加到conf文件夹的mime.types文件中:
text/cache-manifest manifest
manifest文件的写法是先写CACHE MANIFEST,然后换行,每行单列资源文件。每行的换行符可以是CR 、LF或者CRLF——格式很灵活——但文本编码格式必须是UTF-8。UTF-8是多数文本编辑器经常输出的编码格式。
接下来,我们了解一下上面的这个manifest文件示例的各个部分。
如果没有指定标题,默认就是CACHE MANIFEST部分。下面的manifest文件示例中指定了两个要缓存的文件:
CACHE MANIFEST Applicationjs Style.css
添加到CACHE MANIFEST区块中的文件,无论应用程序是否在线,浏览器都会从应用程序缓存中在获取该文件。没有必要在这里列出应用程序的主HTML资源。因为最初指向manifest 文件的HTTL文档会被隐含包含进来。但是,如果希望缓存多个HTML文件,或者希望将多个HTML文件作为支持缓存的应用程序的可选入口,则需将这些文件都列在CACHE MANIFEST中。
FALLBACK部分提供了获取不到缓存资源时的备选资源路径。代码清单10-5所示的manifest文件表明,当无法获取/app/ajax/*时,所有对/app/ajax/及其子路径的请求都会被转发给default.html文件来处理。
代码清单10-5 manifest文件示例(包含所有可能出现的区块)
#comments begin with the hash symbol CACHE MANIFEST # files to cache about.html html5.css index.html happy-trails-rc.gif lake-tahoe.JPG #do not cache signup page NETWORK signup.html FALLBACK signup.html offline.html /app/ajax/ default.html
NETWORK部分罗列的资源,无论缓存中存在与否,均从网络获取
10.2.5 applicationCache API
applicationCache API是一个操作应用缓存的借口。新的window.applicationCache对象可触发一系列与缓存状态相关的事件。该对象有一个数值型属性window.applicationCache.status,代表了缓存的状态。缓存状态共有6种,见表10-2。
事件 | 关联的缓存状态 |
onchecking | CHECKING |
ondownloading | DOWNLOADING |
onupdateready | UPDATEREADY |
onobsolete | OBSOLETE |
oncached | IDLE |
此外,没有可用更新或者发生错误时,还有一些表示更新状态的事件:
- onerror
- onnoupdate
- onprogress
window.applicationCache有一个update()方法会请求浏览器更新缓存。包括检查新版本的manifest文件并下载必要的新资源。如果没有缓存或者缓存已过期,则会抛出错误。
10.3 使用HTML5离线Web应用构建应用
在下面的示例中,我们将跟踪跑步者出发后的位置(包括间断性网络连接或无连接的情况)。比如。 Peter在跑步时将一款具有HTML5 Geolocation功能和支持HTML5 Web浏览器的手机带在身上,不过房子附近树林中的信号不是很好,所以他希望即使无法联网也能定位并记录自身位置。
离线状态下。 Geolocation API 可以在使用硬件地理定位的设备(例如GPS ) 上继续工作,但在使用IP定位的设备上不可以,因为lP地理定位设备需要连接网络,以便将客户端的IP地址映射为坐标。离线应用程序还可以通过像本地存储或者Web SQL Database 这样的接口访问本地存储。图10-2是该应用运行时的截图。
运行此应用程序需要一台Web服务器来提供所需静态资源。需要注意,manifest文件的内容类型必须配置为text/cache-manifest 发送到浏览器。如果文件类型不正确,即使浏览器支持应用缓存也会返回缓存错误。
要运行此应用程序的全部功能。服务器还需要具备接收地理位置数据的功能,本例中,服务器端的主要任务是存储、分析和提供这些数据。静态应用程序中,数据的获取不局限于同源。图10-3是应用程序在Firefox中以离线模式运行时的截图。应用程序的示例文件在本书网站上的”offline ” 部分。
10.3.1 创建记录资源的manifest文件
首先, 按照以下方式创建tracker. manifest文件。文件中列出了应用程序需要缓存的资源:
CACHE MANIFEST # JavaScript ./offline.js #./tracker.js ./log.js # stylesheets ./html5.css # images
10.3.2 创建构成界面的HTML和CSS
下面是本章示例的基本UI结构。因为tracker.html和html5.css会被存储到缓存中,所以应用程序将从缓存中提取这两个文件。
<!DOCTYPE html> <html lang="en" manifest="tracker.manifest"> <head> <title>HTML5 Offline Application</title> <script src="log.js"></script> <script src="offline.js"></script> <script src="tracker.js"></script> <link rel="stylesheet" href="html5.css"> </head> <body> <header> <h1>Offline Example</h1> </header> <section> <article> <button id="installButton">Check for Updates</button> <h3>Log</h3> <div id="info"> </div> </article> </section> </body> </html>
从实现应用程序的离线功能角度来看,这段代码中有两点需要注意。第一点是HTML 元素的manìfest特性。因为<html>元素在HTML5中是可选的,所以本书中大部分的HTML示例都省略了它。不过,如果希望应用程序支持缓存,就不能省略<html>元素,并需要在该元素中设置manifest 特性,因为应用程序是否锺存离线文件取决于是否指定了manifest文件。
第二点要注意的是按钮,它的作用是让用户能够手动安装Web应用程序,以支持离钱情况。
10.3.3 创建离线JavaScript
示例中,JavaScript文件由多个通过<script>标签包含进来的.js文件组成,这些js脚本会同HTML和CSS文件一起存储到缓存中。
<offline.js> /* * log each of the events fired by window.applicationCache */ window.applicationCache.onchecking = function(e) { log("Checking for application update"); } window.applicationCache.onnoupdate = function(e) { log("No application update found"); } window.applicationCache.onupdateready = function(e) { log("Application update ready"); } window.applicationCache.onobsolete = function(e) { log("Application obsolete"); } window.applicationCache.ondownloading = function(e) { log("Downloading application update"); } window.applicationCache.oncached = function(e) { log("Application cached"); } window.applicationCache.onerror = function(e) { log("Application cache error"); } window.addEventListener("online", function(e) { log("Online"); }, true); window.addEventListener("offline", function(e) { log("Offline"); }, true); /* * Convert applicationCache status codes into messages */ showCacheStatus = function(n) { statusMessages = ["Uncached","Idle","Checking","Downloading","Update Ready","Obsolete"]; return statusMessages[n]; } install = function() { log("Checking for updates"); try { window.applicationCache.update(); } catch (e) { applicationCache.onerror(); } } onload = function(e) { // Check for required browser features if (!window.applicationCache) { log("HTML5 Offline Applications are not supported in your browser."); return; } if (!navigator.geolocation) { log("HTML5 Geolocation is not supported in your browser."); return; } if (!window.localStorage) { log("HTML5 Local Storage not supported in your browser."); return; } log("Initial cache status: " + showCacheStatus(window.applicationCache.status)); document.getElementById("installButton").onclick = checkFor; } <log.js> log = function() { var p = document.createElement("p"); var message = Array.prototype.join.call(arguments, " "); p.innerHTML = message; document.getElementById("info").appendChild(p); }
10.3.4 检查applicationCache的支持情况
除了离线应用缓存,示例中还使用了地理定位和本地存储。在页面加载前应确保浏览器支持这两种功能。
onload = function(e) { // Check for required browser features if (!window.applicationCache) { log("HTML5 Offline Applications are not supported in your browser."); return; } if (!navigator.geolocation) { log("HTML5 Geolocation is not supported in your browser."); return; } if (!window.localStorage) { log("HTML5 Local Storage not supported in your browser."); return; } if (!window.WebSocket) { log("HTML5 WebSocket is not supported in your browser."); return; } log("Initial cache status: " + showCacheStatus(window.applicationCache.status)); document.getElementById("installButton").onclick = install; }
10.3.5 为Update按钮添加处理函数
接下来,下面的代码用来处理更新行为,其作用是更新应用缓存:
install = function() { log("Checking for updates"); try { window.applicationCache.update(); } catch (e) { applicationCache.onerror(); } }
单击按钮后将检查缓存区,并更新需要更新的缓存资源。当所有可用更新都下载完毕之后,将在用户界面显示一条消息,告诉用户应用程序已安装成功,可以在离线模式下运行了。
10.3.6 添加Geolocation跟踪代码
下面的代码修改自第4章的地理定位示例代码。它是tracker.js文件的一部分。
/* * Track and report the current location */ var handlePositionUpdate = function(e) { var latitude = e.coords.latitude; var longitude = e.coords.longitude; log("Position update:", latitude, longitude); if(navigator.onLine) { uploadLocations(latitude, longitude); } storeLocation(latitude, longitude); } var handlePositionError = function(e) { log("Position error"); } var uploadLocations = function(latitude, longitude) { var request = new XMLHttpRequest(); request.open("POST", "http://geodata.example.net:8000/geoupload", true); request.send(localStorage.locations); } var geolocationConfig = {"maximumAge":20000}; navigator.geolocation.watchPosition(handlePositionUpdate, handlePositionError, geolocationConfig);
10.3.7 添加storage功能代码
当应用程序处于离钱状态时,需要将数据更新写入本地存储,接下来。我们就添加这方面的代码。
var storeLocation = function(latitude, longitude) { // load stored location list var locations = JSON.parse(localStorage.locations || "[]"); // add location locations.push({"latitude" : latitude, "longitude" : longitude}); // save new location list localStorage.locations = JSON.stringify(locations); }
在第9章中我们介绍了HTML5的localStorage,本章的这个示例应用程序就是通过它来保存坐标的。因为localStorage可以将数据存储在本地浏览器中,所以它特别适用于具有离线功能的应用程序。本地存储中的数据在将来的会话中可用。当网络连接恢复正常后,应用程序就可以与远程服务器进行数据同步。
这里使用Storage还有一个好处,那就是当上传请求失败后可以通过storage得到恢复。如果应用程序遇到某种原因导致的网络错误。或者应用程序被关闭(可能是用户关闭浏览器、浏览器或操作系统崩溃以及页面导航跳转等)的时候,数据会被存储以便下次再进行传输。
10.3.8 添加离线时间处理程序
位置更新处理程序运行时,会去检查网络连接状态。如果应用程序在线,事件处理函数会存储并上传当前坐标。如果应用程序离线,事件处理函数只存储不上传。当应用程序重新连接到网络后,事件处理函数会在UI上显示在线状态,并在后台上传之前存储的所有数据。
window.addEventListener("online", function(e) { log("Online"); }, true); window.addEventListener("offline", function(e) { log("Offline"); }, true);
网络连接状态在应用程序没有真正运行的时候可能会发生改变。例如用户关闭了浏览器、刷新页面或跳转到了其他网站。为了应对这些情况,我们的离钱应用程序在每次页面加载时都会检查与服务器的连接情况。如果连接正常,会尝试与远程服务器同步数据。
// Synchronize with the server if the browser is now online if(navigator.onLine) { uploadLocations(); }
10.4 小结
本章,我们学习了如何基于HTML5离线Web应用创建即使没有因特网连接也可使用的应用程序。为确保应用中所需的文件能够成功缓存,需要将这些文件指定manifest文件中,随后在应用程序的主页面中进行引用。然后, 添加监听器监听在线和离线状态的变化,进而基于因特网连接与否让网站执行不同的操作。
最后一章。我们将对HTML5编程的前景进行展望
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论