如何構建通用存儲中間層

零、問題的由來

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

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

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

那必須滴~,下面開始個人表演~react

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

1、多端統一 api

平常開發中,在不一樣的平臺下因爲有不一樣的存儲層接口,因此每每致使相同邏輯的同一份代碼要寫幾份兒。ios

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

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

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

1.1.異步方法

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

  • save: 異步保存
  • load: 異步讀取

此外還有一些其餘方法:小程序

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

詳情參閱這裏的文檔react-native

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: {},
})
注意:傳遞的是【對象】,而非字符串!

2、支持數據同步

對於一個二次封裝多端存儲層的庫來講,保證多端 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),
})

3、數據過時邏輯

通常各個平臺的存儲層都沒有數據過時這一邏輯。但在使用 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、自動清理過時數據

默認在啓動時會進行一次過時數據清理(能夠關閉),以後每過一段時間會再次清理。

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

4.1.清理邏輯

首先固然是清理已到過時時間的數據,即有一個屬性爲 expires 的數據,且當前時間已超過了該時間。

一旦遇到不知足格式的數據(非對象、沒有 expires 屬性)則跳過,這樣就不會誤清除其餘程序保存的數據。

4.2.清理時間間隔

在初始化時可傳入 autoClearTime 修改默認自動清理時間間隔。

默認爲一分鐘,注意是以秒爲單位。

5、支持永久保存

在某些場景下,可能不方便寫過時時間,這時默承認以傳遞 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',
})

6、支持批量操做

假設如今有一組數據須要保存或讀取,常規操做就是使用 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)

7、小結

還在爲 web 端、小程序端、React-Native 端、node 端業務側代碼使用不同的方式調用存儲層煩惱麼?還在爲手動數據同步,保存數據,處理過時邏輯而煩躁麼?各位開發者老爺們不妨試一試 tua-storage,(擠需體驗三番鍾,裏造會幹我同樣,愛象介款工具)。

貪玩一笑

靈感來源

inspired by react-native-storage

相關文章
相關標籤/搜索