因為項目需要用到 APP 本地存取數(shù)據(jù)庫的功能症杏,所以采用了 realm 的方案,官方文檔地址:https://realm.io/docs/javascript/latest/阴幌。
react-native 存儲方案
- AsyncStorage
react-native 官方存儲方案是 AsyncStorage征绎。AsyncStorage 是一個簡單的、異步的丹泉、持久化的 Key-Value 存儲系統(tǒng)宠进,它對于 App 來說是全局性的晕拆。可用來代替 LocalStorage材蹬。官方推薦使用者在 AsyncStorage 的基礎(chǔ)上做一層抽象封裝,而不是直接使用 AsyncStorage吝镣。
- AsyncStorage 封裝庫
react-native-storage 是一個比較好的框架堤器,我們可以直接使用它,方法很簡單末贾,說明文檔中說得很詳細闸溃。其他庫還有 react-native-simple-store 等。
- realm
realm 是一款專為移動 ? 端開發(fā)的高性能數(shù)據(jù)庫拱撵,其宣稱自己是最快的 react-native 數(shù)據(jù)庫辉川。
realm 整體的優(yōu)點有這么四點:1.簡單易用,2.跨平臺拴测,3.快速性能優(yōu)越乓旗,4.提供高級功能。realm 核心數(shù)據(jù)引擎用 C++ 打造集索,并不是建立在 SQLite 之上的 ORM屿愚。因此性能就是比普通的 ORM 要快很多,甚至比單獨無封裝的 SQLite 還要快务荆。同時因為是 ORM妆距,本身在設(shè)計時也針對移動設(shè)備(iOS、Android)函匕,所以簡單易用娱据,學習成本很低。
realm 常用操作
1盅惜、 作為數(shù)據(jù)庫中剩,使用它無法就是「增刪改查」等基本操作,使用之前酷窥,需要定義 model:
- name:表格名稱咽安。
- primaryKey:主鍵,這個屬性的類型可以是 'int' 和 'string'蓬推,并且如果設(shè)置主鍵之后妆棒,在更新和設(shè)置值的時候這個值必須保持唯一性,并且無法修改。聲明主鍵可以有效地查找和更新對象糕珊,并為每個值強制實現(xiàn)唯一性动分。
- properties:這個屬性內(nèi)放置我們需要的字段。
// models/hello.ts
export const HelloSchema = {
name: "Hello",
primaryKey: "uid", // 定義主鍵后红选,無法創(chuàng)建同一主鍵的數(shù)據(jù)
properties: {
uid: "string",
name: "string", // {type: 'string'} 的簡寫
phone: { type: "string", default: "136xxxxxxxx" }
}
};
2澜公、 初始化
// 根據(jù)提供的表初始化 Realm,可同時往數(shù)組中放入多個表
let realm = new Realm({ schema: [HelloSchema] });
3喇肋、 增加數(shù)據(jù)
對域中對象的更改坟乾、創(chuàng)建、更新和刪除蝶防,必須在 write()事務(wù)塊中進行甚侣。
需要注意:寫入事務(wù)具有不可忽略的開銷,應(yīng)該盡量減少代碼中寫入塊的數(shù)量间学。
try {
realm.write(() => {
realm.create("Hello", {
uid: "a371d56d7b6f77ba31f71d22",
name: "名字1",
phone: "137xxxxxxxx"
});
realm.create("Hello", {
uid: "a371d56d7b6f77ba31f71d22",
name: "名字1",
phone: "137xxxxxxxx"
});
// ...
});
catch(e) {
console.log("Error on creation");
}
定義 model 時我們使用了 primaryKey殷费,如果我們寫入 primaryKey 之前的數(shù)據(jù)中已存在時,我們寫入會出錯低葫,并且如果此時我們寫入的是多個數(shù)據(jù)時详羡,會導致后面的寫入失敗,所以我們可以在單個 realm.create('Hello', item) 的外面使用一個 try...catch...嘿悬。
const result = await new Promise((resolve, reject) => {
try {
realm.write(() => {
for (let item of data) {
try {
realm.create("Hello", item);
} catch (e) {
console.log("write error: ", e);
}
}
resolve("ok");
});
} catch (e) {
console.log("write error: ", e);
resolve("error");
}
});
console.log("write result: ", result);
4实柠、 查找和刪除數(shù)據(jù)
查詢數(shù)據(jù)允許從 realm 獲取單個類型的對象,并可選擇過濾和排序這些結(jié)果鹊漠。最基本方法是使用 objects 方法獲取給定類型的所有對象主到。
let dogs = realm.objects('Dog'); // retrieves all Dogs from the Realm
- Filtering 參數(shù)可過濾查找結(jié)果:
let dogs = realm.objects('Dog').filtered('color = "tan" AND name BEGINSWITH "B"');
- Sorting 參數(shù)允許根據(jù)單個或多個屬性指定排序條件和順序:
let hondas = realm.objects('Car').filtered('make = "Honda"');
// Sort Hondas by mileage
let sortedHondas = hondas.sorted('miles');
// Sort in descending order instead
sortedHondas = hondas.sorted('miles', true);
// Sort by price in descending order and then miles in ascending
sortedHondas = hondas.sorted([['price', true], ['miles', false]]);
- Limiting 參數(shù)提供了對查詢結(jié)果進行“分頁”的能力。這通常是為了避免從磁盤中讀取太多內(nèi)容躯概,或者一次將太多結(jié)果拖入內(nèi)存中登钥。由于 realm 中的查詢是惰性的,因此根本不需要執(zhí)行這種分頁行為娶靡,因為 realm 只會在顯式訪問后從查詢結(jié)果中加載對象牧牢。
let cars = realm.objects('Car'); let firstCars = cars.slice(0, 5);
// 刪除單個數(shù)據(jù)
let result = await new Promise((resolve, reject) => {
try {
realm.write(() => {
for (let uid of data) {
try {
let dataRource = realm.objects("Hello").filtered(`uid = "${uid}"`);
realm.delete(dataRource);
console.log("delete: ", uid);
} catch (e) {
console.log("delete error: ", uid);
}
}
resolve("delete ok!");
});
} catch (e) {
console.log("delete error: ", e);
resolve("delete error!");
}
});
console.log("delete done!: ", result);
// delete all Hello
let result = await new Promise((resolve, reject) => {
try {
let dataRource = realm.objects("Hello");
realm.write(() => {
realm.delete(dataRource);
resolve("delete done!");
});
} catch (e) {
resolve("delete error!");
}
});
console.log("delete done!:", result);
5、 修改數(shù)據(jù)
如果定義 model 時包含主鍵姿锭,則可以讓 realm 根據(jù)其主鍵值智能地更新或添加對象塔鳍。更新時將 true 作為第三個參數(shù)傳遞給 create 方法。
realm.write(() => {
realm.create("Hello", {
uid: "a371d56d7b6f77ba31f71d22",
name: "Recipes",
phone: "135xxxxxxxx"
});
// Update book with new price keyed off the id
realm.create(
"Hello",
{ uid: "a371d56d7b6f77ba31f71d22", phone: "138xxxxxxxx" },
true
);
});
使用報錯記錄
1.安卓打包安裝進入閃退
在 iOS 模擬器和安卓真機調(diào)試都是正常的呻此,打包之后安裝打開一進入就閃退轮纫,通過友盟的錯誤統(tǒng)計發(fā)現(xiàn)報了幾個錯誤(現(xiàn)在找不到記錄了 ??)。
GitHub 里面有對應(yīng)的這些 issue焚鲜,但是沒有人能準確的定位錯誤掌唾,按照別人的建議都沒能解決放前。后來找到一個:
java.lang.ClassNotFoundException: Didn't find class on path: DexPathList - io.realm.react.utils.SSLHelper
試著在 android/app/proguard-rules.pro 加上 -keep class io.realm.react.**
,終于解決了糯彬,原來是打包的時候被混淆引起的凭语。
2.刪除操作記錄時報錯
Accessing object of type X which has been invalidated or deleted
刪除部分數(shù)據(jù)后,必須更新 store 撩扒,如果需要顯示刪除后的數(shù)據(jù)也必須先把 store 中顯示的 realm 數(shù)據(jù)置為空似扔,然后再去重新查詢。
3.安卓調(diào)試模式報錯
DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL
這個可以在這個issue 上找到答案搓谆。