现在的位置: 首页 > WEB前端 > 正文
HTML5离线存储
2014年01月12日 WEB前端 ⁄ 共 7572字 暂无评论 ⁄ 被围观 1,339 views+

原文:Let’s Take This Offline

  什么是离线网络应用程序?乍一看,从以下几个方面来说就像一个矛盾。网页是需下载并呈现的。下载意味着网络连接。你怎能在离线的时候下载?当然,你不能。但你可以在你在线的时候下载。这就是HTML5离线应用程序怎样工作的。

  最简单来说,一个离线网络应用程序就是一个URL的列表——HTML,CSS,JavaScript,图片,或者其他类型的资源。离线网络应用程序的注意指向一个叫做名单文件并用于定位网络服务器上任何文本文件的列表。用于执行HTML5离线以用程序的网络浏览器将从名单文件中读取URL列表,下载这些资源,将他们在本地缓存,并自动在这些本地副本改变时保持他们更新。当你尝试在没有网络连接时访问网络应用程序,你的网络浏览器将自动切换并使用本地代替。

  从现在开始,大多数的工作要靠你网络开发者了。DOM里有一个标记会告诉你在线还是离线。当你的离线状态改变时将会有事件触发(一时离线,下一时在线,或者反之亦然)。但这非常符合情况。如果你的应用程序创建了数据或者存储状态,你该决定当离线时在本地存储数据,且当你重新在线时与远程服务器同步。更新程序在被创建时连接到存储段。换句话说,HTML5可以使你的网络应用程序离线使用。当你处于那种情况该怎么做由你决定。

  离线支持情况

  IE Firefox Safari Chrome Opera iPhone Android

  × √ √ √ × √ √

  谁已经使用离线?

  离线网络应用程序的思想其实要早于HTML5,部分实施也要早些。也就是说,HTML5有办法实现离线,但也有其他办法。我将稍后在本章谈谈其中一个办法:Gears。这些早期使用者中的一部分已经换成了HTML5,并且其他的也正在切换中。

  • Gmail,Google基于网络的邮箱

  • Zoho,在线生产力及合作应用程序online productivity and collaboration apps

  • Remember The Milk,在线任务管理系统

  • WordPress,一个开源的个人发布平台

  缓存名单

  离线网络应用程序与缓存名单文件紧密相联。什么是名单文件?它是你的网络应用程序在失去网络连接时需要访问的所有资源的列表。为了引导下载进程并缓存这些资源,你需要在你的元素中使用manifest属性指向名单文件。

  <!DOCTYPE HTML>

  <html manifest=”/cache.manifest”>

  <body>

  …

  </body>

  </html>

  你的缓存名单文件可以放在你网络服务器的任何地方,但他需要type text/cache-manifest内容类别的支持。如果你正使用基于Apache的网络服务器,你大概只需要在你网络根目录的.htaccess文件中添加一个AddType指令:

  AddType text/cache-manifest .manifest

  然后确认你缓存文件的扩展名为.manifest。如果你使用不同的网络服务器或者不同的Apache配置,请查阅服务器说明文档关于配置Content-Type头的部分。

  FAQ:

  问:我的网络应用程序包含了很多页面。我是否需要在每个页面都使用manifest属性,或者我可以只在主页中使用?

  答:你网络服务器中的每个页面都需要一个指向缓存名单的为全部应用程序准备的manifest属性。

  你的每一个HTML页面指向你的缓存名单文件,并且你的缓存名单文件由合适的Content-Type头支持。但名单文件里有些啥?这是有趣的事情。

  每个缓存名单文件的第一行都是这样的:

  CACHE MANIFEST

  然后,所有的名单文件被分为三个部分:“explicit”段,“fallback” 段,和“online whitelist”段。每个部分有一个标头,单独占一行。如果名单文件不含有任何段落标头,所有列出的资源默认为“explicit”段。尽量不要细想这些术语,以免你崩溃。

  这儿是一个有效的名单文件。它列出了三个资源:一个CSS文件,一个JavaScript文件,和一个JPEG图片。

  CACHE MANIFEST

  /clock.css

  /clock.js

  /clock-face.jpg

  此缓存名单文件没有任何段落头,所以所有列出的资源默认为“explicit”段。在“explicit”段中的资源将会被下载并在本地缓存,且会在你没有网络连接时用于代替它们的在线副本。因此,在下载此名单列表的同时,你的浏览器将会从你网络服务器的根目录下载clock.css,clock.js和clock-face.jpg。然后你可以拔掉你的网线并刷新页面,所有这些资源可以在离线时有效。

  FAQ:

  问:我需要在我的缓存名单中列出我的HTML页面么?

  答:是或者不是。如果你所有的网络应用程序被包含在一个单页面,只需要确认页面通过使用manifest属性指向了缓存名单。当你访问一个含有manifest属性的HTML页面,页面本身被假设为网络应用程序的一部分,所以你不需要将它本身列入名单文件。尽管如此,如果你的网络应用程序包含多个页面,你应该在名单文件中列出所有的HTML页面,否则浏览器将不会知道有其他的HTML页面需要下载并缓存。

  NETWORK段

  这儿是一个稍微复杂一点的例子。Here is a slightly more complicated example. 如果你需要你的计时器应用程序跟踪用户,使用一个从属性动态加载的tracking.cgi脚本。缓存这个资源会使跟踪的目的落空,所以此资源需永不缓存,且在离线时绝对无效。你该这样做:

  CACHE MANIFEST

  NETWORK:

  /tracking.cgi

  CACHE:

  /clock.css

  /clock.js

  /clock-face.jpg

  此缓存名单文件包含了段落头。写着“NETWORK:”的那一行就是“online whitelist”段的开始。在此段落里的资源将永远不会被缓存,且离线时无效。(尝试在离线时加载他们会返回一个错误。)写着“CACHE:”的那一行是“explicit”段的开始。缓存名单文件中剩余的部分和上一个例子一模一样。列出的三个资源都将被缓存切离线时有效。

  FALLBACK段

  这儿还有另一个缓存名单文件的段落类型:“fallback”段。在一个“fallback”段中,你可以为由于任何原因不能被缓存或没有成功被缓存的在线资源定义一个替代文件。HTML5规范提供了一个使用“fallback”段的牛逼例子:

  CACHE MANIFEST

  FALLBACK:

  / /offline.html

  NETWORK:

  *

  这玩意儿干了些啥?首先,考虑一个包含了无数页面的站点,比如Wikipedia。你当然不能下载整个站点,你也应该不想这样。但假设你需要使站点的部分页面在离线时有效,你如何决定缓存哪些页面呢?这样如何:你在一个假定支持离线的Wikipedia站点访问过的每个页面将被下载并缓存。它将包含每个你曾经访问的百科全书条目,每个话题页面(用于对特定百科全书条目的临时讨论),和每个编辑页面(用于修改特定的条目)。

  这就是此缓存名单做的事情。假设Wikipedia的每个HTML页面(条目,话题页面,编辑页面,历史页面)指向一个缓存名单,你的浏览器说道“嘿,此页面是一个离线网络应用程序的一部分,是我知道的那一个么?”如果你的浏览器没有下载这个特定的缓存名单文件,它将建立一个新的离线应用程序缓存,下载缓存名单中列出的所有资源,然后将当前页面加入应用程序缓存。如果你的浏览器识别此缓存名单,它将简单的把当前页面加入已存在的应用程序缓存。总之,你刚访问的页面会在应用程序缓存中结束。这个很重要。这意味着你可以拥有一个在访问时“慢吞吞”添加页面的离线网络应用程序。你不需要在缓存名单中列出你的每一个单独HTML页面。

  现在,看看这个“fallback”段。此缓存名单中的“fallback”段只有一行。此行中的第一部分(空格前)并不是一个URL。它实际上是一个URL模板。此单字符(/)将匹配你站点中的任何页面,不仅仅是首页。当你在离线时尝试访问一个页面,你的浏览器会在应用程序缓存中查找。如果你的浏览器在应用程序缓存中找到此页面(因为你于在线时访问过,且此页面在那时被暗中加入了应用程序缓存中),那么你的浏览器将会显示此页面的缓存副本。如果你的浏览器没有在应用程序缓存中找到此页面,它将显示在“fallback”段那一行第二部分定义的“/offline.html”页面,而不是显示错误信息。

  最后,让我们仔细看看“network”段。此缓存名单中的“network”段也只有一行,只含有一个单字符(*)的一行。此字符在“network”段含有特殊的意义。它被称为“在线白名单通配符”。这个精心设计的方法用于指明,只要你有网络连接,任何不在应用程序缓存中的资源将仍然从原网络地址下载。这对“开放的”离线网络应用程序非常重要。这意味着,当你在线浏览这个假设支持离线的Wikipedia时,你的浏览器将正常的获取图片,视频和其他嵌入的资源,即使他们在另一个域名下。(这在大型网站中很常见,即使它不是离线网络应用程序的一部分。当图片和视频在另一个域的CDN中被执行的同时,HTML页面在本地被生成并工作。)如果没有此通配符,当你在线时,我们设想支持离线的Wikipedia将会行为诡异——它将不会加载任何不同域名下的图片或者视频。

  这例子完整么?不。Wikipedia不仅仅是HTML文件。它在每个页面上使用了通常的CSS,JavaScript,和图片。为了页面在离线时正确的显示和执行,这些资源都需要被明确的列入名单文件的“CACHE:”段中。但“fallback”段的意图是你可以拥有一个开放的离线网络应用程序,而不仅仅限于名单文件中明确列出的资源。

  事件流

  到目前为止,我已经用模糊,半迷惑的术语谈及了离线网络应用程序,缓存名单,和离线应用程序缓存。资源被下载,浏览器做出判断,且一切运行正常。你懂的,对吧?我是说,这就是我们谈及的网络开发。什么也没有,仅仅是正常运行。

  首先,让我们谈一下事件流。特别是DOM事件。当你的浏览器访问一个指向了缓存名单的页面时,它在window.applicationCache对象上触发了一连串的事件。我想这看起来很复杂,但相信我,这是我能提供的含有所有重要信息的最简版本。

  1. 一旦你的浏览器注意到元素含有manifest属性,它将触发一个checking 事件。(所有这儿列出的事件均在window.applicationCache对象上触发。)checking事件总是被触发的,不管你之前是否访问过此页面,或者任何其它指向同一缓存名单的页面。

  2. 如果你的浏览器不曾遇见过此缓存名单…

  o 它将触发一个downloading事件,然后开始下载此缓存名单中列出的资源。

  o 在下载的同时,你的浏览器将会周期性的触发progress事件,此事件包含了诸如多少文件已被下载,多少文件仍然处于下载队列等信息。

  o 当缓存名单中所有列出的资源被成功下载后,浏览器触发最后的一个事件,cached。 这是你的离线网络应用程序被完整缓存并以备离线使用的标志。就这样,你完工了。

  3. 另一方面,如果你之前访问过此页面或者其他页面指向了相同的缓存名单,那么你的浏览器已经知道这个缓存名单。可能已经有一些资源在应用程序缓存中。可能全部工作的网络应用程序已在应用程序缓存中。现在的问题是,你的浏览器上次检测之后,缓存名单是否已经更改?

  o 如果答案是没有,缓存名单没有更改,你的浏览器将会立即触发一个noupdate事件。就这样,你完工了。

  o 如果答案是有,缓存名单已有更改,你的浏览器将会触发一个downloading事件,并开始重新下载缓存名单中列出的每个资源。

  o 在下载的同时,你的浏览器将会周期性的触发progress事件,此事件包含了诸如多少文件已被下载,多少文件仍然处于下载队列等信息。

  o 当缓存名单中所有列出的资源被成功重新下载后,浏览器触发最后的一个事件,updateready。这是你新版本的离线网络应用程序被完整缓存并以备离线使用的标志。新版本不会立即被使用。为了即刻使用新版本而不强制用户重载页面,你可以手动调用window.applicationCache.swapCache()函数。

  如果此过程中的任何一点出现可怕的错误,你的浏览器将会触发一个error事件,并立即终止。这是一个可能引发错误的完整且简短的列表:

  • 缓存名单返回一个HTTP404错误(页面未找到),或者410错误(永久消失)。

  • 缓存名单被找到且没有更改,但指向名单的HTML页面没有正确下载。

  • 缓存名单被找到且被更改,但浏览器没有下载某个缓存名单中列出的资源。

  调试的艺术

  我想在此说出两件重要的事情。第一件事是你刚刚阅读过的,但我猜想你没有真正的充分理解,所以再说明一下:如果任何一个在你缓存名单中列出的资源没有正确下载,整个获取离线网络应用程序的进程将会失败。你的浏览器将会触发一个error事件,但不会有迹象表明具体是哪种问题。这可能让调试离线网络应用程序变得十分崩溃。

  第二件重要的事情从技术层面说,并不是一个错误,但它看起来像是一个严重的浏览器错误直到你明白发生了什么。它与你浏览器怎样检测缓存名单是否被更改有点确切的关系。这是一个三相的进程。这令人烦恼,但很重要,所以要注意。

  1. 通过标准的HTTP语义,你的浏览器将会检测缓存名单是否已经过期。就像任何其他由HTTP服务的文件,你的网络服务器将会包含典型的关于此文件在HTTP响应头中的元信息。这些HTTP头中的一些(Expires和Cache-Control)将告诉你的浏览器如何允许缓存文件而不询问服务器此文件是否已更改。此种类型的缓存和离线网络应用程序没有任何关系。它发生在几乎每个HTML页面,样式表,图片或者其他网络资源。

  2. 如果缓存名单已过期(根据它的HTTP头),那么你的浏览器将会询问服务器是否有新版本,如果有,浏览器将会下载它。要做到这一点,你的浏览器产生一个包含此缓存名单last-modified数据的HTTP请求,你的网络服务器将浏览器下载名单文件的最后时间包含在HTTP响应头中。如果网络服务器判断从那个时间之后没有被更改,它将简单的返回一个304(未改变)状态。同样的,这不是离线网络应用程序所特有的。它发生于实质上每种类型的网络资源。

  3. 如果网络服务器认为名单文件在那个时间之后有被更改,它将返回一个200(OK)HTTP状态码,后面是新文件的内容和新的Cache-Control头,以及一个新的last-modified时间,因此,第1步和第2步将可能在下次发生。(HTTP非常酷,网络服务器总是为将来做打算。如果你的网络服务器绝对需要给你传送一个文件,他尽所有可能确认他不需要无故传送第二次。)一旦下载了新的缓存名单文件,你的浏览器将根据它上次下载的副本检测内容。如果缓存名单文件的内容跟上次的一样,你的浏览器将不会重新下载此名单中列出的任何资源。

  当你开发和测试你的离线网络应用程序时,这些步骤的任意一个都可能让你犯错误。例如,比如说你发布了一个新版本的缓存名单文件,10分钟后,你发现你需要在里面添加另一个资源。没问题,对吧?仅仅添加另一行并重新发布。这是将要发生的事情:你重新载入页面,你的浏览器发现了manifest属性,它触发了checking事件,然后…没啥了。你的浏览器坚持认为缓存名单文件并没有被更改。为啥?因为你的网络服务器可能由于默认配置,告诉浏览器需要缓存静态文件几个小时(通过HTTP语义,使用Cache-Control头)。这意味着你的浏览器将不会通过三相进程中的第1步。当然,网络服务器知道文件已经更改,但你的浏览器甚至不会有足够的时间去询问网络服务器。为啥?因为你的浏览器上次下载了缓存名单,网络服务器告诉它需要缓存这个资源几个小时(通过HTTP语义,使用Cache-Control头)。那么10分钟后,这就是你的浏览器确切会做的事情。

  你必须清楚,这不是错误,而只是个特性。一切都按照它应该的方式工作着。如果网络服务器没有办法告诉浏览器(和中间代理)去缓存资源,网络可能很快崩溃。但没人来安慰你花上几个小时去尝试想出为啥你的浏览器没有注意到你更新过的缓存名单。(实际上,如果你等待得足够久,它将神秘重新开始工作!因为HTTP缓存过期了!就像它应该的那样!杀了我!现在杀了我!)

  所以,这儿有一件你必须要做的事情:重新配置你的网络服务器,以便你的缓存名单文件不会因为HTTP语义而可缓存。如果你使用基于Apache的网络服务器,你.htaccess文件中的这两行将会达到目的:

  ExpiresActive On

  ExpiresDefault “access”

  这将会使每一个在此目录和所有其子目录中的文件缓存失效。这可能是你在制作中所不希望的,所以你应该使用一个指令来限制它,以致其只对你的缓存名单文件有效,或者创建一个只包含这个.htaccess文件和缓存名单文件的子目录。通常,配置细节随网络服务器而变化,所以查阅你的服务器说明文档来确认如何控制HTTP缓存头。

  一旦你使缓存名单文件本身的HTTP缓存失效,你将仍然看到过时的却已在应用程序缓存中更改的某个资源,只因为它仍然以相同的URL存在于你的网络服务器。这里,三相进程中的第2步将会欺骗你。如果你的缓存名单文件没有被更改,浏览器将不会注意到之前缓存的某个资源已被更改。注意下面的例子:

  CACHE MANIFEST

  # rev 42

  clock.js

  clock.css

  如果你更改了clock.css并重新发布,你不会看到更改,因为缓存名单文件本身没有更改。每次你对离线应用程序中的某个资源进行更改,你需要对缓存名单文件本身做出修改。这跟改变一个单字符一样简单。我发现完成这件事最简单的方法就是包含一个有修订编号的注释行。更改这个注释中的修订编号,那么网络服务器将返回最新更改的缓存名单文件,并且将使进程开始重新下载名单中列出的所有资源。

  CACHE MANIFEST

  # rev 43

  clock.js

  clock.css

给我留言

您必须 [ 登录 ] 才能发表留言!