iOS的“Web App”与移动Safari有不同的localStorage

16

我有一个带有以下 meta 标签的 iPad 网页应用:

<meta name="apple-mobile-web-app-capable" content="yes">

当我从主页打开应用程序(web-app-capable版本)或在Mobile Safari中输入地址时,localStorage的内容是不同的。通过打印location.href,我确认地址是相同的。

在使用Mobile Safari时对localStorage所做的所有更改都会反映在web-app-capable版本中,但是在web-app-capable版本中所做的更改不会反映在Mobile Safari版本中。

域名是相同的,localStorage应该是相同的。到底发生了什么?这能被修复吗?


更新-解决方案:按照接受答案的建议2(强制用户处于全屏模式),我添加了以下代码:

if(("standalone" in window.navigator) && !window.navigator.standalone)
    window.location = "instructions.html";

所以,如果您正在使用支持独立模式的浏览器,并且您不在独立模式下,请重定向到一个页面(instructions.html),该页面向用户展示如何将应用程序添加到主屏幕。

感谢所有人的意见!


当你说网页应用时,它是一个保存为主屏幕应用程序的书签吗?还是一个基本上是Webview的应用程序,你将其包装并作为应用程序运行? - Nitin Alabur
这是一个书签,保存在主屏幕上的应用程序中,但使用该元标记时,它不会在Safari中打开,而是作为全屏应用程序在自己的Safari实例中运行。因此,它既是保存在主屏幕上的书签,又是基本上作为应用程序运行的Web视图。 - Juliet
另外,我应该注意 - 没有那个元标记,这不是问题,因为应用程序只是在常规移动Safari的新标签页中打开。 - Juliet
从前,我曾以这种方式测试过BoA移动网站,并注意到在Safari中加载URL和在Webview中作为应用程序包装的URL之间的性能差异相当大。 Safari更快。因此,我认为在(Web App vs Mobile Safari)中加载网页并不相同。我不确定为什么会有差异。 - Nitin Alabur
移动版Safari浏览器拥有更快的JavaScript引擎。我不确定为什么它没有被启用于Web应用,但很可能是出于兼容性方面的考虑。 - ThomasW
请注意下面的注释,关于iOS 6不再允许Safari和Web应用程序之间共享本地存储。iOS 5允许这样做,因此某些应用程序可能需要进行重构。http://stackoverflow.com/questions/11545149/ios-web-app-has-different-localstorage-than-mobile-safari#comment-17056733 - Dylan Valade
3个回答

32

摘要:

Safari和全屏web应用程序(即web-app-capable)具有单独的localStorage数据内存写入缓存。每次全屏应用程序变为活动状态时,它会从磁盘重新加载localStorage(允许它查看Safari的更改)。但是,当Safari变为活动状态时,它不会重新加载来自磁盘的localStorage数据,因此除非您杀死Safari并重新启动它,否则它将无法看到在全屏应用程序中进行的更改。

完整解释:

计算机科学中只有两个难题:

  1. 缓存失效
  2. 命名事物
  3. off-by-one错误

localStorage中存在问题的行为是问题#1导致的。以下是原因:

当iOS浏览器引擎加载时,它从磁盘中读取localStorage的数据并将其缓存在内存中。然后,每次读取数据(例如getItem)时,数据都从内存中读取,而不是从磁盘中读取;写入(例如setItem)时,数据被写入内存中,然后(异步地)刷新到磁盘中。由于localStorage是同步的,所以这种实现是完全合理的。如果对所有读取和写入都进行磁盘操作,则您的JavaScript将在每次读/写时被阻止以执行昂贵的磁盘IO。

问题是全屏web应用程序(我们称之为FSWA)使用iOS浏览器引擎的单独实例,虽然FSWA共享localStorage数据的同一磁盘位置,但它与Safari不共享localStorage数据的内存缓存。

当您考虑到FSWA完全重新加载(这意味着从磁盘重新加载localStorage数据)每次它们成为活动应用程序时,您会看到您所看到的行为。

这是幕后故事...

  1. 用户在Safari中更改数据并将其写入localStorage
  2. Safari将数据写入其内存中的localStorage缓存
  3. Safari将localStorage数据从缓存刷新到磁盘
  4. 用户离开Safari并启动FSWA
  5. FSWA负责将localStorage数据从磁盘加载到内存缓存中
  6. 用户在Safari中看到被更改的数据(步骤#1中)
  7. 用户在FSWA中进行更改,写入数据到localStorage
  8. FSWA将数据写入其localStorage缓存(Safari的缓存未被更新)
  9. FSWA将其localStorage缓存数据刷新到磁盘中
  10. 用户切换回Safari
  11. Safari已经运行,并且不会重新加载来自磁盘的localStorage数据
  12. Safari从其现有的内存缓存中读取旧数据
  13. 用户在步骤#7中所做的更改不会被看到

为了证明这一点,您可以在步骤#4和步骤#10之间关闭Safari。然后在步骤#11中重新启动Safari,它将重新加载来自磁盘的localStorage数据,并且您将看到由FSWA编写的数据。


10
随着iOS6的推出,一些变化已经发生。现在,全屏网络应用程序的数据已经完全与Safari分开(就像本地应用程序一样)。请参阅http://blog.nsbasic.com/?p=928。 - two-bit-fool

5
在iOS5中,我能够在同一域上拥有两个全屏Web应用程序,它们能够看到彼此的localStorage。这克服了全屏和Safari之间的差异。
但是,在iOS6中,我必须将我的两个全屏Web应用程序合并为一个应用程序。

3
假设您正确保存了本地存储数据,如果我没有误解,那么您经历的是Web应用程序开发人员中相当普遍的问题。 Cookie、会话和本地存储似乎在“Web应用程序”(从主屏幕启动)中与通过移动Safari保存的数据存储方式不同。
我过去进行了一些相当彻底的测试,对我来说似乎没有足够好的解决方法。举个例子,我和我的同事们最近开发了一个Web应用程序,在访问所有功能之前,用户必须登录。如果通过移动Safari登录,然后切换到下载版本的应用程序,则希望已登录,但并非总是如此。通常需要重新登录,这表明Cookie可能存储在不同的“数据库”中,具体取决于您选择从何处或从何处启动应用程序。
此外,正如Calvin所说的那样,与仅仅不同的数据库相比,还有更多需要考虑的因素。通过主屏幕启动的应用程序似乎打开速度较慢,主屏幕应用程序在启动时总是重新加载,这表明没有多任务支持等。我的结论是:启动下载的Web应用程序并不等同于Safari减去地址栏,因此不应将其视为这样。

虽然这是苹果的一个不错的功能,但主屏幕上的Web应用程序并没有像人们期望的那样运行(就像在Safari中打开一样)。在您的情况下,假设您正确存储了LS数据并尝试了不同的方法来解决您特定的问题,我建议采用以下其中之一:

  1. 使用mysql数据库进行读写操作
  2. 强制用户下载应用程序后才能使用它(例如在this example中)
  3. 不要鼓励用户下载应用程序,并假设他们大多数人将从移动Safari访问它
  4. 接受这个事实,数据可能会有所不同(根据您的应用程序性质,这可能不是一个替代方案)
  5. 采取我的方法,通过Phonegap内置功能将您的Web应用程序"转换"为本机应用程序。如果是这样,请查看Jonathan Stark的this tutorial

希望这至少有助于澄清其中的部分内容。


PhoneGap 看起来需要很多工作,而且我没有自由去做假设或接受这个项目中的不一致性。而且它必须可以离线缓存,所以 mysql 不是一个选择。所以我选择了第二种方式,而它真的非常简单!谢谢! - Juliet
@Jonathan,第一个链接已经失效了,你能否更新一下你的答案?:) - Sofía

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