如何正确使HTML5缓存清单失效以供在线/离线Web应用程序使用?

55

我目前正在使用缓存清单(如此处所述)。这实际上使得应用程序必需的资源在用户离线时可用。

然而,它的效果有点过于显著。

缓存清单加载后,Firefox 3.5+将缓存缓存清单中明确引用的所有资源。但是,如果服务器上的文件被更新,并且用户在在线状态下尝试强制刷新页面(包括缓存清单本身),Firefox将绝对拒绝获取任何东西。应用程序仍然完全冻结在最后一个被缓存的点。问题如下:

  1. 我希望Firefox只在网络连接失败时才有效地依赖缓存资源。我尝试过使用 FALLBACK 块,但无济于事。这是否可能?
  2. 如果 #1 不可行,则用户是否有办法强制刷新页面并绕过此缓存(ctrl-F5无效,清除浏览器缓存也无效)而不清除其私人数据?或者,缓存清单机制是否支持过期标头,并且关于它的行为是否有记录在案的文档?

1
我曾经看到过这个示例问题间歇性地发生。通常,更新清单中的文件并在清单中更新注释中的版本号会导致重新加载更新后的文件,但有时Firefox会卡住并拒绝重新加载新资源,尽管清单没有任何问题。我发现唯一的解决方法是清除离线缓存,但这对于部署更新来说是不可接受的。 - Michael
6个回答

25
我认为我已经弄清楚了:如果缓存清单中有错误(比如说,引用的文件不存在),那么Firefox将完全停止处理任何与应用程序缓存相关的内容。这意味着它不会更新缓存中的任何内容,包括您缓存的缓存清单。
为了发现这个问题,我从Mozilla 借来了一些代码,并将其放入了一个新的(非缓存)HTML文件中。最终记录的消息指出我的缓存清单可能存在问题,果然是这样(一个缺失的文件)。

// Convenience array of status values
var cacheStatusValues = [];
 cacheStatusValues[0] = 'uncached';
 cacheStatusValues[1] = 'idle';
 cacheStatusValues[2] = 'checking';
 cacheStatusValues[3] = 'downloading';
 cacheStatusValues[4] = 'updateready';
 cacheStatusValues[5] = 'obsolete';

 // Listeners for all possible events
 var cache = window.applicationCache;
 cache.addEventListener('cached', logEvent, false);
 cache.addEventListener('checking', logEvent, false);
 cache.addEventListener('downloading', logEvent, false);
 cache.addEventListener('error', logEvent, false);
 cache.addEventListener('noupdate', logEvent, false);
 cache.addEventListener('obsolete', logEvent, false);
 cache.addEventListener('progress', logEvent, false);
 cache.addEventListener('updateready', logEvent, false);

 // Log every event to the console
 function logEvent(e) {
     var online, status, type, message;
     online = (isOnline()) ? 'yes' : 'no';
     status = cacheStatusValues[cache.status];
     type = e.type;
     message = 'online: ' + online;
     message+= ', event: ' + type;
     message+= ', status: ' + status;
     if (type == 'error' && navigator.onLine) {
         message+= ' There was an unknown error, check your Cache Manifest.';
     }
     log('<br>'+message);
 }

 function log(s) {
    alert(s);
 }

 function isOnline() {
     return navigator.onLine;
 }

 if (!$('html').attr('manifest')) {
    log('No Cache Manifest listed on the  tag.')
 }

 // Swap in newly download files when update is ready
 cache.addEventListener('updateready', function(e){
         // Don't perform "swap" if this is the first cache
         if (cacheStatusValues[cache.status] != 'idle') {
             cache.swapCache();
             log('Swapped/updated the Cache Manifest.');
         }
     }
 , false);

 // These two functions check for updates to the manifest file
 function checkForUpdates(){
     cache.update();
 }
 function autoCheckForUpdates(){
     setInterval(function(){cache.update()}, 10000);
 }

 return {
     isOnline: isOnline,
     checkForUpdates: checkForUpdates,
     autoCheckForUpdates: autoCheckForUpdates
 }

这确实很有帮助,但我应该向Mozilla请求一个功能,即至少将格式不正确的缓存清单打印到错误控制台。诊断像重命名文件这样微不足道的问题不应需要自定义代码附加到这些事件上。

2
你可以使用 Manifesto 书签工具来验证你的缓存清单。 - Maarten

16

我使用了来自HTML5 Rocks: 更新缓存的代码:

window.addEventListener('load', function(e) {
  if (window.applicationCache) {
    window.applicationCache.addEventListener('updateready', function(e) {
        if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
          // Browser downloaded a new app cache.
          // Swap it in and reload the page to get the new hotness.
          window.applicationCache.swapCache();
          if (confirm('A new version of this site is available. Load it?')) {
            window.location.reload();
          }
        } else {
          // Manifest didn't changed. Nothing new to server.
        }
    }, false);
  }
}, false);

1
这似乎需要更改清单。仅更新文件不会触发更新。 - Anthony Graglia
谢谢,这是我发现的更新应用程序的唯一方法,如果你正在缓存主index.html。 - evilcelery
我建议使用window.location.reload(true),因为文档指定了“true-从服务器加载内容”。 - JakubKnejzlik

8
我有同样的问题:一旦Firefox保存了离线文件,它就永远不会重新加载它们。Chrome按预期工作,它检查清单文件是否更改,如果清单文件更改,则重新加载所有内容。Firefox甚至没有从服务器下载清单文件,因此无法注意到更改。
经过调查,我发现Firefox正在缓存缓存清单文件(旧式缓存,而不是离线缓存)。将清单文件的缓存标头设置为Cache-Control: no-cache, private解决了这个问题。

@brianl 我撤销了你批准的编辑。元标签在这里不适用,因为缓存清单不是一个HTML文件。 - OlliM

7

免责声明:我在清单和缓存方面的经验仅适用于Safari和FF可能处理一些不同的事情。

  1. 你是对的。如果清单上列出的任何文件都找不到,就不会进行缓存。

  2. 即使您在线,浏览器也只会检查清单文件。在等待清单文件时,它将继续从缓存中加载站点 - 这样不会延迟呈现 - 但这意味着您在第一次加载时看不到任何更改。

  3. 下次加载站点时,如果清单在上次加载时更改,则会加载新文件。

总是需要重新加载两次才能看到任何更改。实际上,有时我需要重新加载3次才能看到更新。不知道为什么。

在调试时,我使用php动态生成我的清单文件,因此不存在文件名拼写错误的机会。我还每次随机生成版本号以强制更新,但仍然可以脱机测试Web应用程序。

完成后,php文件可以仅使用恒定的版本号回显保存的清单数据,并始终使用缓存。

这是我最近玩清单和缓存时学到的一些东西。它非常有效,但可能会令人困惑。

没有到期日。要取消缓存,必须将清单文件更改为空并重新加载。在Safari上,清除用户缓存确实会清除所有缓存文件。


镜像我的FireFox体验几乎完全一样,除了在Safari中清除用户缓存似乎会清除HTML缓存。感谢您的输入。 - Justin Searls
我曾经遇到过缓存过度的问题,而且清除缓存也很困难。在Safari上(无论是在台式机还是移动设备上)有好几次我都不得不打开一个新标签页(在台式机上)或者重启iPad设备,以便在某些莫名其妙的情况下强制重新加载。我发现捕获所有更新事件对于识别这个问题非常有用(例如,在开始检查更新但从未触发“更新完成”、“进度”或“错误”事件时,我立即知道这是客户端的问题,而不是应用程序的问题)。 - Iain Collins
哦,有趣的是,在Firefox(可能也适用于其他浏览器)中,更改注释掉的清单行(这是苹果开发者文档推荐的方法)不会导致触发缓存更新。您必须添加或删除具有活动指令的行(例如实际上添加或从缓存清单中删除文件条目),然后它才能识别到清单已更新。 - Iain Collins

3
我制作了一个 Firefox 插件,可以使缓存清单失效并清除 HTML5 本地存储。

http://sites.google.com/site/keigoattic/home/webrelated#TOC-Firefox-HTML5-Offline-Cache-and-Loc

您还可以通过在错误控制台中输入以下代码来使缓存清单失效:
// invalidates the cache manifest
var mani = "http://.../mysite.manifest"; // manifest URL
Components.classes["@mozilla.org/network/application-cache-service;1"].getService(Components.interfaces.nsIApplicationCacheService).getActiveCache(mani).discard();

或者,通过在地址栏中输入以下代码,可以手动强制更新缓存:

javascript:applicationCache.update()

谢谢!您的插件安装有点困难,但是它运行得非常好! - Abel Tamayo
谢谢!在about:config中的Firebug控制台中写入这个就可以了! - Jan Wikholm
你能在文件的JavaScript中使用那行代码,以便在用户在线时更新缓存,但离线时显然不会更新吗?还是说这行代码会在离线时导致代码出错? - Anthony Graglia

2

嗯,我刚才在修改清单文件后调用了缓存的update()方法,接收到了完整的检查/下载/准备序列,进行了一次重新加载,在我的应用程序的初始页面中,我所做的一个js文件的文本更改迅速出现。

看来我只需要重新加载一次。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接