开发者问题收集

为什么在可用性检查之后,MacOS/iOS 设备中的 localStorage 有时为空?

2021-03-09
4981

我在使用本地存储的 Web 应用程序中遇到了一个问题,虽然不是很频繁,但仍会定期发生。

此问题仅发生在 iOS/macOS 设备上,与浏览器无关,记录的错误如下:

TypeError: 'null' is not an object (evaluating 'localStorage.getItem')

代码的工作原理如下;有一个函数 ( isLocalStorageSupported ) 用于检查是否支持本地存储,还有另一个函数 ( getLocalData ) 在尝试从存储中获取数据之前使用它。

在检查本地存储完成后返回 true 时会抛出错误,但下一行在尝试从本地存储中获取密钥时莫名其妙地(?)抛出错误,因为该阶段的存储变量似乎为空。

我不清楚这是怎么发生的,因此我提出了这个问题。

// The function that checks availability
const isLocalStorageSupported = (() => {
  let localStorageSupported = null;

  return () => {
    if (localStorageSupported !== null) {
      return localStorageSupported;
    }

    try {
      localStorage.setItem('check', 'check');
      localStorage.getItem('check');
      localStorage.removeItem('check');

      localStorageSupported = true;
    } catch (error) {
      localStorageSupported = false;
      console.error('localStorage not supported');
    }

    return localStorageSupported;
  };
})();

const getLocalData = (key) => {
  if (isLocalStorageSupported()) { // This check is successful

    let data = localStorage.getItem(key); // This line throws a TypeError!

    if (!data) {
      return;
    }

    return data;
  }
};
3个回答

如果项目不存在,localStorage 将 不会 抛出错误。它将返回 null:

//Purpose of this is to generate a completely random key to prove my point
let randomKey = String.fromCharCode(...crypto.getRandomValues(new Uint8Array(100))).match(/[A-Z]|[a-z]|[0-9]/g).join('');

//Get the value associated with the key (there is none)
console.log(localStorage.getItem(randomKey)); //--> null

如果您需要检查浏览器是否支持 localStorage,请通过以下方式检查:

let localStorageSupported = 'localStorage' in window;
//this is equal to !!window.localStorage or Boolean(window.localStorage)

if (!localStorageSupported) {
    alert('Get a new browser!');
}
quicVO
2021-03-12

从技术上讲:您正在缓存检查结果,并返回缓存的结果。

如果 localStorage 在第一次调用 isLocalStorageSupported() 时有效,之后变为无效,而无需重新加载页面,您的函数仍将返回 true

“无效”可能是内置 localStorage 对象状态的变化,或者更可能是某些函数与全局 localStorage 对象混淆。

额外一点,可能不相关但值得注意:
您的代码片段显示了对名为 localStorage 的非限定变量的访问(例如:不是 window.localStorage ),它可以由您的 javascript 框架中的某些代码提供或处理。


最简单的建议是不缓存结果。

为了避免破坏 localStorage 中可能的 “check” 键,您可以使用更具特色的键名(例如: “__my_company_check__” )。

您可以确认您的问题是否来自此缓存行为(注意:此建议仍然在每个调用站点引入未缓存的检查,它将允许您确定您的问题是否来自缓存测试值):

function uncachedCheck() { ... }
function cachedCheck() { ... }

if (uncachedCheck() !== cachedCheck()) {
    alert('state of localStorage has changed !')
    // feed extra information to Splunk ...
}

我没有好的建议来修复检查的缓存版本:也许检查 localStorage 是否仍然是同一个对象?(但我不知道不同的浏览器如何支持 localStorage

let localStorageSupported = null;
let checkedValue = undefined;

try {
if ((localStorage === checkedValue) && (localStorageSupported !== null)) {
    return localStorageSupported;
}

checkedValue = localStorage;
...
LeGEC
2021-03-19

由于它仅适用于 iOS(您确定所有 macOS 浏览器都适用吗?),这可能是智能跟踪预防:

https://webkit.org/tracking-prevention/

Therefore, ITP deletes all cookies created in JavaScript and all other script-writeable storage after 7 days of no user interaction with the website.

charlax
2024-02-16