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

Posted

技术标签:

【中文标题】如何正确地使在线/离线 Web 应用程序的 HTML5 缓存清单失效?【英文标题】:How to properly invalidate an HTML5 Cache Manifest for online/offline web apps? 【发布时间】:2010-12-15 11:50:05 【问题描述】:

我目前正在使用缓存清单(如 here 所述)。这有效地使用户离线时运行应用程序所需的资源可用。

不幸的是,它的效果有点太好了。

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

    我希望 Firefox 在网络连接失败时有效地仅依赖缓存的资源。我试过使用 FALLBACK 块,但无济于事。这甚至可能吗? 如果 #1 不可能,用户是否可以强制刷新页面并绕过此缓存(ctrl-F5 不会这样做,也不会清除浏览器的缓存,令人震惊)除了清除他们的私人数据?或者,缓存清单机制是否支持过期标头,它的行为是否记录在任何地方?

【问题讨论】:

我已经看到这个示例问题间歇性地发生。通常更新清单中的文件,然后更新清单中注释的修订号会导致重新加载更新的文件,但偶尔 Firefox 会卡住并拒绝重新加载新资源,尽管清单没有任何问题。我发现解决此问题的唯一方法是清除离线缓存,但部署更新不可接受。 【参考方案1】:

我想我已经弄清楚了:如果缓存清单中有错误(例如,引用的文件不存在),那么 Firefox 将完全停止处理任何与 applicationCache 相关的内容。这意味着,它不会更新缓存中的任何内容,包括缓存的缓存清单。

为了发现这是问题所在,我borrowed some code from 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(''+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 请求一个功能,该功能至少可以将格式错误的缓存清单打印到错误控制台。它不应该需要自定义代码附加到这些事件来诊断像重命名文件一样微不足道的问题。

【讨论】:

您可以使用Manifesto 小书签来验证您的缓存清单。【参考方案2】:

我使用了来自HTML5 Rocks: Update the cache的代码:

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);

【讨论】:

这似乎需要更改清单。单独更新文件不会触发更新。 谢谢,如果您正在缓存主 index.html,这是我发现的唯一更新应用程序的方法 我建议使用 window.location.reload(true) 作为文档指定“true - 从服务器加载内容”【参考方案3】:

我遇到了同样的问题:一旦 Firefox 保存了离线文件,它就永远不会重新加载它们。 Chrome 按预期工作,它检查清单文件是否有更改,并在清单文件更改时重新加载所有内容。 Firefox 甚至没有从服务器下载清单文件,所以它不会注意到变化。

经过调查,我发现 Firefox 正在缓存缓存清单文件(老式缓存,而不是离线缓存)。将manifest文件的缓存头设置为Cache-Control: no-cache, private即可解决问题。

【讨论】:

@brianl 我回滚了您批准的编辑。元标记在这里不适用,因为缓存清单不是 html 文件。【参考方案4】:

免责声明:我在清单和缓存方面的经验都是 Safari 和 FF 可能会以不同的方式处理某些事情。

    你说的很对。如果清单中列出了任何找不到的文件,则不会进行缓存。

    即使您在线,浏览器也只会检查清单文件。在等待清单文件时,它会继续从缓存中加载网站——这样就不会延迟渲染——但这意味着你在第一次加载时看不到任何变化。

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

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

调试时,我使用 php 即时生成清单文件,因此文件名中不会出现拼写错误。我也每次随机生成版本号来强制更新,但仍然有一个离线 webapp 进行测试。

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

我最近在玩清单和缓存时学到了一些东西。效果很好,但可能会令人困惑。

没有过期。要取消缓存,您必须将清单文件更改为其中没有任何内容并重新加载。在 Safari 上,清除用户缓存会清除所有缓存的文件。

【讨论】:

几乎完全反映了我在 FireFox 中的体验,除了在 Safari 中清除用户缓存显然会清除 HTML 缓存。感谢您的意见。 我在过度缓存方面有过非常相似的经历,它拒绝清除缓存。在 Safari 中(在桌面和移动设备上)有很多次我不得不在浏览器中打开一个新选项卡(在桌面上)或重新启动设备(在 iPad 上)以在它“卡住”时强制重新加载“出于某种深不可测的原因。我发现捕获所有更新事件对于识别这一点非常有用(例如,当它开始检查更新但从不触发“更新完成”、“进度”或“错误”事件时,我马上就知道这是客户端的问题,而不是应用程序)。 哦,还有 Firefox(可能是其他浏览器)中有趣的事情 - 更改清单中的注释行(Apple 开发人员文档推荐的方法)不会导致它触发缓存更新。您必须添加或删除带有活动指令的行(例如,实际从缓存清单中添加或删除文件的条目),然后它才能识别清单已更新。【参考方案5】:

我制作了一个 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()

【讨论】:

谢谢!你的插件有点难安装,但是效果很好! 谢谢!在 about:config 的 Firebug 控制台中编写它就可以了! 您可以在文件的 javascript 中使用该行,以便在用户在线时缓存更新,但在离线时显然不会更新?还是离线时换行会破坏代码?【参考方案6】:

嗯,我刚刚在缓存上调用了 update(),在对清单文件进行了编辑更改之后,收到了检查/下载/准备好的完整序列,重新加载了一次,我在一次中进行了文本更改我的 js 文件,显示在我的应用程序的初始页面中,立即出现。

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

【讨论】:

以上是关于如何正确地使在线/离线 Web 应用程序的 HTML5 缓存清单失效?的主要内容,如果未能解决你的问题,请参考以下文章

如何在有在线交易时向离线应用发送通知

如何正确地使表单或窗口控件 RecreateWnd 感知?

如何在android应用程序中以在线和离线模式加载或检索网页?

JS学习21(离线应用与客户端储存)

如何在 Android 中检测设备状态离线/在线

如何在带有 PostgreSQL 数据库的 Web 应用程序中拥有完整的离线功能?