基本介绍
简介 : xj.storage 是一个关于本地存储的插件,它对 localStorage
和 sessionStorage
这两个对象的内容进行了一些扩展,使用该插件有以下好处,首先是它在操作时全程使用了 try…catch
容错处理,可避免存储操作出错后逻辑被卡住的危险,其次是它借助了 JSON
对数据格式进行了转存,使得存储不再局限于字符串,最后是它修复了许多浏览器的 BUG,使得存储操作变得更加统一和安全。
兼容 : IE10+ / Edge12+ / Firefox / Chrome / Safari / Opera / IOS Webkit / Android Platform
更新 : https://github.com/xjZone/xj.storage/blob/master/upgrade.md
源码 : https://github.com/xjZone/xj.storage/
协议 : Apache License 2.0
版本 :
引入插件
首先是引入相关的文件,推荐使用带 immutable 的 JSDelivr CDN:
xj.storage.localStorage
和 localStorage
相关的属性与方法都被设置在了 xj.storage.localStorage
对象上,实际上这个调用确实是太长了,所以建议在使用时先进行简化,例如用 var xjls = xj.storage.localStorage;
或 xj.ls = xj.storage.localStorage;
的写法,缩短后再使用,此外就是 xj.storage.localStorage
对象和 xj.storage.sessionStorage
对象的属性与方法都是一样的,只在 Storage
事件的响应方面略有区别,关于事件的这个区别我们最后会再讲到,下面我们主要以 xj.storage.localStorage
对象的内容来做示范。
.set()
该方法类似 localStorage.setItem()
方法,但是它会用借助 JSON
对数据进行转存,所以能够支持多种数据类型,而不是仅限于字符串,由于 JSON
需要考虑跨语言的兼容性,所以它并不支持其他语言没有的数据类型(如 undefined
),以下是支持和转换的细节:
Number
=> 支持,但是 NaN
和 Infinity
等特殊值会被转成 null
。
String
=> 支持,任意的字符串都是可以的。
Boolean
=> 支持。
Object
=> 支持,但非简单对象会被转成空对象,不被支持的键值对则会直接忽略。
Array
=> 支持,但数组中的元素如果是非法值,将会被转成 null
。
null
=> 支持,没有目标值的时候就是返回 null
。
undefined
=> 不支持,默认情况下直接存储将变成 null
,在数组中存储也会变成 null
,在对象中存储则会被忽视。
Function
=> 不支持,默认情况下直接存储将变成 null
,在数组中存储也会变成 null
,在对象中存储则会被忽视。
Symbol
=> 不支持,默认情况下直接存储将变成 null
,在数组中存储也会变成 null
,在对象中存储则会被忽视。
BigInt
=> 不支持,存储将失败,如果你确实有这种需求,也许可以考虑先转 String
,获取时再转回 BigInt
。
语法 : xj.storage.localStorage.set(key, value)
key
: Required | String
| 键名,禁止设置 constructor
或 toString
等原形对象上已经存在的键名
value
: Required | Anytype
| 结果,Undefined
和 Function
和 Symbol
和 BigInt
都不被支持
.get()
该方法类似于 localStorage.getItem()
方法,但是它会用借助 JSON
对结果进行解析,所以可得到各种数据类型的值,该方法的第二个参数既 defaultValue
用于设置一个默认值,当 Storage 操作出错或目标值并不存在时,就会得到这个默认值,默认为 null
。
语法 : xj.storage.localStorage.get(key[, defaultValue])
key
: Required | String
| 键名,如果目标并不存在则返回 defaultValue
defaultValue
: Optional | Anytype
| 默认值,当出错或目标值不存在时返回该值,默认为 null
.remove()
该方法类似于 localStorage.removeItem()
方法,用于移除目标键值对,该方法没有返回值,即使被移除的目标不存在也不会报错。
.clear()
该方法类似 localStorage.clear()
方法,用于清空 localStorage
,但是将操作放在了 try…catch
中,避免因为出错而卡住。
.length()
该方法类似 localStorage.length
属性,只是将操作放在了 try…catch
中,避免因为出错而卡住,如果出错了则总是返回 0
。
.key()
该方法类似 localStorage.key()
方法,传入索引值后会返回目标位置的 key
值,但需要注意的是,排序未必和存入的顺序相同。
.byte()
该方法用于获取 localStorage
存储数据的总字节数,要注意的是这个字节数是包括 value
和 key
的,而不是只计算 value
。
.error
该属性用于保存操作出错时的错误对象,默认为 null
,它会保存最近的一个错误,对于一些关键操作,你可以在操作后就判断,如果这个属性不为 null
,那就表示出错了,得进行容错处理,之后就得将这个属性再设置为 null
,否则可能会影响到之后的操作判断。
.support
该属性用于判断浏览器是否支持 localStorage
对象和它的属性与方法,支持返回 true
,否则返回 false
,由于插件做了容错处理,所以即使返回 false
,操作无法运行也不会卡住 JS,只是功能会无效化,不支持的情况多种多样,可参考 XJ 写的 这篇文章。
.listener
该属性用于记录 on()
方法绑定的事件监听,关于该方法和 Storage
事件的绑定,下一章就会提到了,这个属性是个数组,元素都是对象,结构为 {key:String, callback:Function}
,当使用了 off()
方法进行事件移除时,这个数组中对应的元素也会被删掉。
.on()
该方法用于绑定 Storage
事件,被监听的 key
在发生变化的时候就会触发事件,与原生的 Storage
事件相比,该方法解决了 IE 浏览器中的一些兼容问题,并且 Storage
事件会在所有的页面中触发,包括操作数据的页面也会触发该事件,虽然按照标准,导致数据变化的那个页面不该响应这个事件的,如果你还是希望跟标准保持一致,那么可通过全局配置的 dispatchOriginal
参数进行修改。
语法 : xj.storage.localStorage.on(key, callback)
key
: Required / String
/ 要监听变化的目标 key
值
callback
: Required / Function
/ 回调,函数还有个 event
参数
.off()
该方法用于解绑 Storage
事件,解绑后目标 key
在发生变化的时候就不会再响应事件了,它可以接受两个参数,但只有第一个参数是必填项,如果只传入了第一个参数,那么所有符合该参数的事件都会被解绑,如果还传入了第二个参数,那么就只有那些既符合 key
参数又符合 callback
参数的事件才会被解绑,on()
方法可执行多次监听同个 key
值,依靠第二个参数可更加精准的进行解绑。
语法 : xj.storage.localStorage.off(key[, callback])
key
: Required | String
| 将要被解绑的目标 key
字符串
callback
: Optional | Function
| 有传入该参数则该参数也对得上才会解绑
跨标签响应的 Storage 事件
下面这段代码同时存在于 demo_01_localStorage_testEvent.html 和 demo_02_localStorage_testEvent.html 这两个页面,同时打开它们并点击按钮,就能看到 Storage
事件的跨标签响应了,两个页面的信息会自动同步,需要注意,点击 off 按钮解绑事件后,该页面就不会再响应 Storage
事件了,但其他页面会继续响应,也就是说事件虽然可以跨标签响应,但绑定和解绑还是需要逐个页面操作。
xj.storage.sessionStorage
xj.storage.sessionStorage
的属性和方法跟 xj.storage.localStorage
完全相同,所以这里就不重复了,但它们的 Storage
事件有些区别(原本就有区别,和 xj.storage 没关系),具体来讲就是 localStorage
的事件会在同源的其他页面里触发,包括其他标签页面和 iframe
框架页面,但 sessionStorage
的事件只会在同一个页面里的同源 iframe
框架页面中触发,无法跨标签响应。
下面这段代码同时存在于 demo_04_sessionStorage_testEvent.html 和 demo_05_sessionStorage_testEvent.html 这两个页面,同时打开它们并点击按钮,可以发现同一个页面里的同源 iframe
页面会同步响应数据变化,但另一个标签页面则不会同步数据的变化,下面这段代码和上面那段代码是完全相同的,只是 localStorage
被改成了 sessionStorage
,简写也从 xjls
变化为 xjss
而已。
全局配置
全局配置对 xj.storage.localStorage
对象和 xj.storage.sessionStorage
对象都会生效,需要注意的是,全局配置得在引入插件前就设置好,否则插件加载并执行的时候找不到配置,就会认为配置不存在,并且版本号还要对得上,否则插件也不会理会的,例如本页面使用的插件是 0.2.2 的版本,在 xj.storageConfig
对象后面跟着的就是 ['0.2.2']
,版本匹配是考虑到多版本并存而设计的。
项目备注
01. IE11 在存入大额数据时,会出现不响应 Storage
事件的 BUG,这个大额数据的具体额度,在不同设备环境中不尽相同,详情可参考 IE11 doesn't fire local storage events when value is too large,XJ 曾尝试参考前面这个页面的说法,使用代理键值对的做法来解决这问题,但后来发现这种做法虽然能让 Storage
事件响应,但也会导致无法获取到准确的 newValue
属性,所以最后并没采用该方案,这问题也就没能解决,除非你并不需要兼容 IE11,否则在保存大额数据时,要记得这个数据可能不会引发 Storage
事件。
02. IE 和 Safari(MacOS) 的 Storage
事件会在操作数据的页面也触发,这是不标准的,为了能够判断该事件是否由当前页引发,所以 xj.storage 为每个页面都创建了 id,并在操作数据时将 id 也进行了保存,之后在响应 Storage
事件时再借助 id 进行判断,问题就得到了解决,但由于数据带上了 id,所以字节数会变多,使用 byte()
方法时会得到一个比预想中更大的值,就是这个原因了。
03. IE 存在着操作异步的问题,由于它会在数据还未正式写入文件时就执行回调,所以在 Storage
事件回调中使用 get()
方法获取值,可能得到的是触发事件前的旧值,但是你可以用 event
对象的 newValue
属性来获取新结果值,这属性的返回值总是对的。
04. 有疑问或者发现 BUG,可到 GitHub 提 Issue,如果觉得插件写得还行,在 GitHub 中为本项目点个 ★Star 吧,感谢啦 ಠ‿ಠ ❤。
推荐阅读
XJ.Chen - 漫谈 WebStorage API 既本地存储的 8 个 Feature 以及 20 个 BUG