零、问题的由来
开门见山地说,这篇文章【又】是一篇安利软文~,安利的对象就是 tua-storage。
顾名思义,这就是一款存储数据的工具。
用 tua-storage 好处大大的有么?
那必须滴~,下面开始我的表演~
- 多端统一 api
- 支持数据同步
- 数据过期逻辑
- 自动清理过期数据
- 支持永久保存
- 支持批量操作
一、多端统一 api
日常开发中,在不同的平台下由于有不同的存储层接口,所以往往导致相同逻辑的同一份代码要写几份儿。
例如,小程序中保存数据要使用【异步】的 wx.setStorage
、wx.getStorage
或对应的同步方法;
而在 web 端使用 localStorage 的话,则是【同步】的 setItem
、getItem
等方法;
在 React-Native 的场景下,使用的又是 AsyncStorage
中【异步】的 setItem
、getItem
...
1.1.异步方法
然而,经过 tua-storage
的二次封装,以上两个方法统一变成了:
-
save
: 异步保存 -
load
: 异步读取
此外还有一些其他方法:
-
clear
: 异步清除(删除多个) -
remove
: 异步删除(删除单个) -
getInfo
: 异步获取信息(如keys
)
1.2.同步方法
在某些场景下正好需要调用同步方法的话,咋办咧?
与 Node.js 的 api 风格差不多,在上述异步方法后面加上 Sync
就是对应的同步方法:
saveSync
loadSync
clearSync
removeSync
getInfoSync
那么在 AsyncStorage
的场景下,压根就没有同步方法时调用以上方法会怎么样呢?
嗯,你猜得没错,会直接报错...
1.3.区分场景
如何区分不同的场景呢?
在初始化的时候传递 storageEngine
即可:
import TuaStorage from 'tua-storage'
const tuaStorage = new TuaStorage({
// 小程序
storageEngine: wx,
// web
storageEngine: localStorage,
// React-Native
storageEngine: AsyncStorage,
// Node.js
storageEngine: {},
})
注意:传递的是【对象】,而非字符串!
二、支持数据同步
对于一个二次封装多端存储层的库来说,保证多端 api 的统一仅仅是常规操作而已。
tua-storage
的另一大亮点就是数据同步功能。
想想平时我们是怎么使用存储层的
- 读取一个数据
-
正好存储层里有这个数据
- 返回数据(皆大欢喜,happy ending~)
-
假如存储层里没这个数据
- 手动调用各种方法去同步这个数据
- 手动存到存储层中,以便下次读取
各位有没有看出其中麻烦的地方在哪儿?数据同步部分的复杂度全留给了业务侧。
让我们回归这件事的【初心】:我仅仅需要获取这个数据!我不管它是来自存储层、来自接口数据、还是来自其他什么地方...
2.1.数据同步函数
因此 tua-storage
在读取数据时很贴心地提供了一个 syncFn
参数,作为数据同步的函数,当请求的数据不存在或已过期时自动调用该函数。并且数据同步后默认会保存下来,这样下次再请求时存储层中就有数据了。
syncParams
的使用场景是接口需要传参时,这些参数会传给 syncFn
。
tuaStorage.load({
key: 'some data',
syncFn: ({ a }) => axios('some api url' + a),
// 以下参数会传到 syncFn 中
syncParams: { a: 'a' },
})
这么一来,存储层就和接口层对接起来了。业务侧再也不用手动调用 api 获取数据。
2.2.合并分散配置
每次读取数据时如果都要手动传同步函数,实际编码时还是很麻烦...
不急,吃口药~
tua-storage
在初始化时能够传递一个叫做 syncFnMap
参数。顾名思义,这是一个将 key
和 syncFn
映射起来的对象。
const tuaStorage = new TuaStorage({
// ...
syncFnMap: {
'data one': () => axios('data one api'),
'data two': () => axios('data two api'),
// ...
},
})
// 不用手动传 syncFn,默认匹配 syncFnMap 中的对应函数
tuaStorage.load({ key: 'data one' })
2.3.自动生成配置
其实手动编写每个 api 请求函数也是很繁琐的,要是有个根据配置自动生成请求函数的库就好了~
诶~,巧了么不是~。各位开发者老爷们了解一下同样跨平台的 tua-api ~?
tua-storage
搭配 tua-api
之后会变成这样
import TuaStorage from 'tua-storage'
import { getSyncFnMapByApis } from 'tua-api'
// 本地写好的各种接口配置
import * as apis from '@/apis'
const tuaStorage = new TuaStorage({
syncFnMap: getSyncFnMapByApis(apis),
})
三、数据过期逻辑
一般各个平台的存储层都没有数据过期这一逻辑。但在使用 tua-storage
时默认每个数据都有过期时间这一属性。
3.1.默认过期时间
默认为 30 秒,可以在初始化时配置默认超时时间。
import TuaStorage from 'tua-storage'
const tuaStorage = new TuaStorage({
// 改为 60 秒
defaultExpires: 60,
})
// 返回一个 Promise
tuaStorage
.save({
key: 'data key',
data: { foo: 'bar' },
// 这里传递的过期时间优先级更高
expires: 90,
})
.then(console.log)
.catch(console.error)
// 保存到 storage 中的数据大概长这样
// key 之前会加上初始化传入的默认前缀
{
'TUA_STORAGE_PREFIX: data key': {
expires: 90,
rawData: { foo: 'bar' },
},
}
3.2.数据保存前缀
为了保证存在 storage 中的数据名称不冲突,以及实现版本控制,tua-storage
默认有一个存储前缀:storageKeyPrefix
。
默认值为 TUA_STORAGE_PREFIX:
,所以在上一小节中保存的数据会有一个奇怪的前缀。
保证名称不冲突很好理解,如何实现版本控制呢?
3.3.白名单机制
clear
函数能够接受一个白名单数组(因为内部是通过 indexOf
来判断的,所以不必填写完整的 key
值)。
import TuaStorage from 'tua-storage'
const tuaStorage = new TuaStorage({ ... })
tuaStorage.clear(['key'])
.then(console.log)
.catch(console.error)
// 假设现在 storage 中有以下数据
{
'foo': {},
'bar': {},
'foo-key': {},
'bar-key': {},
}
// 清除后剩下的数据是
{
'foo-key': {},
'bar-key': {},
}
所以在调用 clear
时,在白名单中传入新的存储前缀,即可实现删除上一版本数据的功能。
import TuaStorage from 'tua-storage'
// 上一版本的前缀
const prefix1 = 'STORAGE_PREFIX_V1.0: '
// 这一版本的前缀
const prefix2 = 'STORAGE_PREFIX_V1.1: '
const tuaStorage = new TuaStorage({
// 将默认前缀切换成新版本的
storageKeyPrefix: prefix2,
})
// 开始清除上个版本的数据
tuaStorage.clear([ prefix2 ])
.then(console.log)
.catch(console.error)
四、自动清理过期数据
默认在启动时会进行一次过期数据清理(可以关闭),之后每过一段时间会再次清理。
什么样的数据会被清理呢?
4.1.清理逻辑
首先当然是清理已到过期时间的数据,即有一个属性为 expires
的数据,且当前时间已超过了该时间。
一旦遇到不满足格式的数据(非对象、没有 expires
属性)则跳过,这样就不会误清除其他程序保存的数据。
4.2.清理时间间隔
在初始化时可传入 autoClearTime
修改默认自动清理时间间隔。
默认为一分钟,注意是以秒为单位。
五、支持永久保存
在某些场景下,可能不方便写过期时间,这时默认可以传递 expires: null
,标记该数据永不过期。
不喜欢用 null
标记?
大丈夫~,初始化时传递 neverExpireMark
即可修改为你喜欢的别的标记。
import TuaStorage from 'tua-storage'
const tuaStorage = new TuaStorage({
neverExpireMark: 'never',
})
// 永不过期
tuaStorage.save({
key: 'some key',
data: 'some data',
expires: 'never',
})
六、支持批量操作
假设现在有一组数据需要保存或读取,常规操作就是使用 Promise.all
发起多个操作。
import TuaStorage from 'tua-storage'
const tuaStorage = new TuaStorage({ ... })
const dataToBeSaved = [
{ key: 'key one', data: 'some data' },
{ key: 'key two', data: 'some data' },
]
// 异步
const result = dataToBeSaved
.map(tuaStorage.save.bind(tuaStorage))
.then(Promise.all.bind(Promise))
// 同步
const result = dataToBeSaved
.map(tuaStorage.saveSync.bind(tuaStorage))
讲道理这样写还是挺烦的...所以 tua-storage
的各个 api 还支持直接传入数组:
// 异步
tuaStorage.save(dataToBeSaved)
.then(console.log)
.catch(console.log)
// 同步
tuaStorage.saveSync(dataToBeSaved)
七、小结
还在为 web 端、小程序端、React-Native 端、node 端业务侧代码使用不一样的方式调用存储层烦恼么?还在为手动数据同步,保存数据,处理过期逻辑而烦躁么?各位开发者老爷们不妨试一试 tua-storage,(挤需体验三番钟,里造会干我一样,爱象介款工具)。
灵感来源
inspired by react-native-storage
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。