React Native中Storage使用詳解和封裝
在移動端開發中,數據庫存儲肯定是避免不了的需求,在iOS中,我們也經常使用NSUserDefault單利類來存儲一些簡單的用戶信息等數據,在web開發中我們經常使用LocalStorage
來存儲簡單數據,在React Native中,我們可以選擇直接使用官方推薦的數據存儲組件AsyncStorage
組件,但是有時候使用起來還是不夠簡單,功能不夠多,這時我們就會選擇封裝一個storage,我們選擇使用三方的react-native-storage
來進一步封裝
react-native-storage 官方文檔
https://github.com/sunnylqm/react-native-storage/blob/master/README-CHN.md
作者提供封裝好的storage組件Demo示例
https://github.com/guangqiang-liu/react-native-storage-Demo
安裝react-native-storage組件
npm install react-native-storage --save
import Storage from 'react-native-storage'
import { AsyncStorage } from 'react-native'
import {sync} from './sync'
如何封裝
這里封裝storage,我們根據官方文檔的建議,我們將采用單利模式來進行封裝,單利模式對于iOS開發的同學們肯定不陌生,其實web開發也有單例模式,是常用開發模式中的一種
- 初始化
首先我們得創建一個storage全局單利對象
代碼如下:
const createStorage = () => {
storage = new Storage({
// 最大容量,默認值1000條數據循環存儲
size: size,
// 存儲引擎:對于RN使用AsyncStorage,對于web使用window.localStorage
// 如果不指定則數據只會保存在內存中,重啟后即丟失
storageBackend: AsyncStorage,
// 數據過期時間,默認一整天(1000 * 3600 * 24 毫秒),設為null則永不過期
defaultExpires: defaultExpires,
// 讀寫時在內存中緩存數據。默認啟用。
enableCache: true,
// 如果storage中沒有相應數據,或數據已過期,
// 則會調用相應的sync方法,無縫返回最新數據。
// sync方法的具體說明會在后文提到
// 你可以在構造函數這里就寫好sync的方法
// 或是在任何時候,直接對storage.sync進行賦值修改
// 或是寫到另一個文件里,這里require引入
sync: sync
})
}
- 判斷這個storage單利對象是否存在,不存在就創建一個
const initStorage = () => {
if (!storage) {
createStorage()
}
}
- 完善storage 增刪改查等功能API
核心代碼
const _storage = {
// 使用key來保存數據。這些數據一般是全局獨有的,常常需要調用的。
// 除非你手動移除,這些數據會被永久保存,而且默認不會過期。
save(key, obj) {
initStorage()
storage.save({
key: key, // 注意: 請不要在key中使用_下劃線符號!
data: obj,
// 如果不指定過期時間,則會使用defaultExpires參數
// 如果設為null,則永不過期
expires: defaultExpires
})
},
// 取數據
load(key, callBack) {
initStorage()
storage.load({
key: key,
// autoSync(默認為true)意味著在沒有找到數據或數據過期時自動調用相應的sync方法
autoSync: true,
// syncInBackground(默認為true)意味著如果數據過期,
// 在調用sync方法的同時先返回已經過期的數據。
// 設置為false的話,則始終強制返回sync方法提供的最新數據(當然會需要更多等待時間)。
syncInBackground: true,
// 你還可以給sync方法傳遞額外的參數
syncParams: {
extraFetchOptions: { // 各種參數
},
someFlag: true,
}
}).then(ret => {
// 如果找到數據,則在then方法中返回
// 注意:這是異步返回的結果(不了解異步請自行搜索學習)
// 你只能在then這個方法內繼續處理ret數據
// 而不能在then以外處理
// 也沒有辦法“變成”同步返回
// 你也可以使用“看似”同步的async/await語法
callBack && callBack(ret)
return ret
}).catch(err => {
//如果沒有找到數據且沒有sync方法,
//或者有其他異常,則在catch中返回
console.warn(err.message);
switch (err.name) {
case 'NotFoundError':
// TODO
break
case 'ExpiredError':
// TODO
break
}
})
},
// 獲取某個key下的所有id(僅key-id數據)
getIdsForKey(id, callback) {
initStorage()
storage.getIdsForKey(id).then(ids => {
callback && callback(ids)
})
},
// 獲取某個key下的所有數據(僅key-id數據)
getAllDataForKey(key, callback) {
initStorage()
storage.getAllDataForKey(key).then(users => {
callback && callback(users)
})
},
// !! 清除某個key下的所有數據(僅key-id數據)
clearMapForKey(key) {
initStorage()
storage.clearMapForKey(key)
},
// 刪除單個數據
remove(key) {
initStorage()
storage.remove({
key: key
})
},
// !! 清空map,移除所有"key-id"數據(但會保留只有key的數據)
clearMap() {
initStorage()
storage.clearMap()
}
}
- 導出單利類
export {_storage as storage}
注意: 上面的基本使用方式官方文檔也都說的很詳細了,這里重點介紹下,new Storage
中的 sync: sync
這個用法是說:調用storage.load時,如果本地并沒有存儲相應的user數據,那么會自動觸發storage.sync.user去遠程網絡請求獲取user數據并無縫返回過來
- 創建sync 文件,同步遠程數據(刷新)
核心代碼
/**
* Created by guangqiang on 2017/11/15.
*/
import {storage} from './index'
/**
* sync方法的名字必須和所存數據的key完全相同
* 方法接受的參數為一整個object,所有參數從object中解構取出
* 這里可以使用promise。或是使用普通回調函數,但需要調用resolve或reject
* @type {{user: ((params))}}
*/
const sync = {
user(params) {
let { id, resolve, reject, syncParams: { extraFetchOptions, someFlag } } = params
fetch('http://www.baidu.com', {
method: 'GET',
body: 'id=' + id,
...extraFetchOptions,
}).then(response => {
return response.json()
}).then(json => {
//console.log(json)
if(json && json.user){
storage.save({
key: 'user',
data: json.user
})
if (someFlag) {
// 根據syncParams中的額外參數做對應處理
}
// 成功則調用resolve
resolve && resolve(json.user)
} else {
// 失敗則調用reject
reject && reject(new Error('data parse error'))
}
}).catch(err => {
console.warn(err)
reject && reject(err)
})
}
}
export {sync}
附加功能
- 讀取批量數據
// 使用和load方法一樣的參數讀取批量數據,但是參數是以數組的方式提供。
// 會在需要時分別調用相應的sync方法,最后統一返回一個有序數組。
storage.getBatchData([
{ key: 'loginState' },
{ key: 'checkPoint', syncInBackground: false },
{ key: 'balance' },
{ key: 'user', id: '1009' }
])
.then(results => {
results.forEach( result => {
console.log(result);
})
})
//根據key和一個id數組來讀取批量數據
storage.getBatchDataWithIds({
key: 'user',
ids: ['1001', '1002', '1003']
})
.then( ... )
這兩個方法除了參數形式不同,還有個值得注意的差異。getBatchData會在數據缺失時挨個調用不同的sync方法(因為key不同)。但是getBatchDataWithIds卻會把缺失的數據統計起來,將它們的id收集到一個數組中,然后一次傳遞給對應的sync方法(避免挨個查詢導致同時發起大量請求),所以你需要在服務端實現通過數組來查詢返回,還要注意對應的sync方法的參數處理(因為id參數可能是一個字符串,也可能是一個數組的字符串)
使用方式
import {storage} from './storage'
componentDidMount() {
let obj = {}
obj.name = '張三'
obj.age = 20
obj.sex = 'man'
// 存
storage.save('userInfo', obj)
// 取
storage.load('userInfo', (data) => {
alert(data.name)
})
}
更多文章
- 作者React Native開源項目OneM【500+ star】地址(按照企業開發標準搭建框架完成開發的):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
- 作者簡書主頁:包含60多篇RN開發相關的技術文章http://www.lxweimin.com/u/023338566ca5 歡迎小伙伴們:多多關注,多多點贊
- 作者React Native QQ技術交流群:620792950 歡迎小伙伴進群交流學習
- 友情提示:在開發中有遇到RN相關的技術問題,歡迎小伙伴加入交流群(620792950),在群里提問、互相交流學習。交流群也定期更新最新的RN學習資料給大家,謝謝大家支持!