零、問題的由來

開門見山地說,這篇文章【又】是一篇安利軟文~,安利的對象就是

tuateam/tua-storage?

github.com
圖標

顧名思義,這就是一款存儲數據的工具。

用 tua-storage 好處大大的有么?

那必須滴~,下面開始我的表演~

  • 多端統一 api
  • 支持數據同步
  • 數據過期邏輯
  • 自動清理過期數據
  • 支持永久保存
  • 支持批量操作

一、多端統一 api

日常開發中,在不同的平台下由於有不同的存儲層介面,所以往往導致相同邏輯的同一份代碼要寫幾份兒。

例如,小程序中保存數據要使用【非同步】的 wx.setStoragewx.getStorage 或對應的同步方法;

而在 web 端使用 localStorage 的話,則是【同步】的 setItemgetItem 等方法;

在 React-Native 的場景下,使用的又是 AsyncStorage 中【非同步】的 setItemgetItem...

1.1.非同步方法

然而,經過 tua-storage 的二次封裝,以上兩個方法統一變成了:

  • save: 非同步保存
  • load: 非同步讀取

此外還有一些其他方法:

  • clear: 非同步清除(刪除多個)
  • remove: 非同步刪除(刪除單個)
  • getInfo: 非同步獲取信息(如 keys

實例方法 | tua-storage?

tuateam.github.io

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 參數。顧名思義,這是一個將 keysyncFn 映射起來的對象。

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)

默認配置 | tua-storage?

tuateam.github.io

四、自動清理過期數據

默認在啟動時會進行一次過期數據清理(可以關閉),之後每過一段時間會再次清理。

什麼樣的數據會被清理呢?

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

sunnylqm/react-native-storage?

github.com
圖標

推薦閱讀:
查看原文 >>
相关文章