为什么在可用性检查之后,MacOS/iOS 设备中的 localStorage 有时为空?
我在使用本地存储的 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;
}
};
如果项目不存在,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!');
}
从技术上讲:您正在缓存检查结果,并返回缓存的结果。
如果
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;
...
由于它仅适用于 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.