数据持久化
-
数据持久化一直都是软件开发中重要的一个环节,几乎所有的应用都具备这一项功能;那什么是数据持久化呢?—— 说白了就是数据的本地化存储,将数据存储到本地,在需要的时候进行调用。
-
这边我们介绍两种在 React-Native 中比较常用的存储方式
- AsyncStorage:这是官方使用的存储方式,类似于 iOS 中的
NSUserDefault
,区别在于,AsyncStorage
只能存储 字符串键值对,而NSUserDefault
可以存储 字符串和number。 - Realm:今天才发现 Realm 也已经支持 React-Native ,这是新兴的移动端数据存储方式,在没有它之前,一直都是使用 sqlist 进行数据存储,在性能上,各有优势,但是操作上,Realm 有着明显优势,更方便使用。
- AsyncStorage:这是官方使用的存储方式,类似于 iOS 中的
-
接下来我们就来看看怎么使用它们。
AsyncStorage 简单使用
-
AsyncStorage方法官方文档写得很详细,这边就不对赘述了!
-
AsyncStorage 使用方法很简单,我们就直接上代码:
// 增加 createData() { AsyncStorage.setItem('name', JSON.stringify('吉泽明步'), (error, result) => { if (!error) { this.setState({ data:'保存成功!' }) } }); } // 查询 inquireData() { AsyncStorage.getItem('name') .then((value) => { let jsonValue = JSON.parse((value)); this.setState({ data:jsonValue }) }) } // 更新 upData() { AsyncStorage.setItem('name', JSON.stringify('苍井空'), (error, result) => { if (!error) { this.setState({ data:'更新成功!' }) } }); } // 删除 removeData() { AsyncStorage.removeItem('name'); this.setState({ data:'删除完成!' }) }
-
按照官方推荐,我们使用 AsyncStorage 前,最好进行一层封装,React-Native中文网 给我们提供了一个比较好的框架 —— react-native-storage,我们可以直接使用它,方法很简单,说明文档中说得很详细。
-
既然是第三方框架,那么第一部肯定就是导入到我们的工程中:
npm install react-native-storage --save
- 接着,我们根据创建一个
Storage
文件专门对框架进行初始化操作:
import { AsyncStorage, } from 'react-native'; // 第三方框架 import Storage from 'react-native-storage'; var storage = new Storage({ // 最大容量,默认值1000条数据循环存储 size: 1000, // 存储引擎:对于RN使用AsyncStorage,对于web使用window.localStorage // 如果不指定则数据只会保存在内存中,重启后即丢失 storageBackend: AsyncStorage, // 数据过期时间,默认一整天(1000 * 3600 * 24 毫秒),设为null则永不过期 defaultExpires: 1000 * 3600 * 24, // 读写时在内存中缓存数据。默认启用。 enableCache: true, // 如果storage中没有相应数据,或数据已过期, // 则会调用相应的sync方法,无缝返回最新数据。 // sync方法的具体说明会在后文提到 // 你可以在构造函数这里就写好sync的方法 // 或是写到另一个文件里,这里require引入 // 或是在任何时候,直接对storage.sync进行赋值修改 sync: require('./sync') }) // 全局变量 global.storage = storage;
-
到这里,我们需要注意的就是要在哪里初始化这个文件,其实一个思路就是 —— 在哪个地方,我们只需要引用一次文件,就可以在其他文件中使用(比如:我们程序默认的进口就是
index.ios/android.js
文件,那么只要在他们中引用一次文件即可,这样就不需要去注意什么调用顺序,因为index.ios/android.js
文件肯定是最先调用的,它们才是真正的王)。 -
然而,为了方便我们使用同一套代码,我们会创建一个
Main
文件作为程序入口的中转总站
来管理其他的文件,然后外界只要调用这个Main
文件,就可以展示里面的所有东西。所以,将引用
放到Main
文件中是最好的选择。
// 在 main 文件中添加 import storage from '封装的文件位置';
-
到这里,我们就完成了最基础的配置,我们只需要在需要用到的地方直接使用就可以了,首先我们在新建一个文件,然后从Main文件跳转到这个文件中。
-
接着,我们就真正地自己来使用一下这个框架:
// 增加 createData() { // 使用key保存数据 storage.save({ key:'storageTest', // 注意:请不要在key中使用_下划线符号! rawData: { name:'吉泽明步', city:'xx省xxx市' }, // 设为null,则不过期,这里会覆盖初始化的时效 expires: 1000 * 3600 }); } // 查询 inquireData() { storage.load({ key:'storageTest', // 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语法 // 更新data值 this.setState({ data: ret.name }); }).catch(err => { //如果没有找到数据且没有sync方法, //或者有其他异常,则在catch中返回 console.warn(err.message); switch (err.name) { case 'NotFoundError': // 更新 this.setState({ data:'数据为空' }); break; case 'ExpiredError': // TODO break; } }) } // 更新 upData() { // 重新存储即可 storage.save({ key:'storageTest', // 注意:请不要在key中使用_下划线符号! rawData: { name:'苍井空', city:'xx省xxx市' }, // 设为null,则不过期,这里会覆盖初始化的时效 expires: 1000 * 3600 }); } // 删除 removeData() { // 删除单个数据 storage.remove({ key: 'storageTest' }); // storage.remove({ // key: 'react-native-storage-test', // name:'吉泽明步' // }); // // !! 清空map,移除所有"key-id"数据(但会保留只有key的数据) // storage.clearMap(); // // // 获取某个key下的所有id // storage.getIdsForKey('user').then(ids => { // console.log(ids); // }); // // // 获取某个key下的所有数据 // storage.getAllDataForKey('user').then(users => { // console.log(users); // }); // // // !! 清除某个key下的所有数据 // storage.clearMapForKey('user'); }
- 很简单对不,那对于 react-native-storage 的使用就先讲到这里。
Realm 配置与常见错误处理
-
很惊喜,Realm 也支持了 React-Native ,这样我们可以在移动端
愉快地
进行存储操作了。 -
而且使用方法 Realm 官方提供的文档都一如既往地详细,所以如果感兴趣,也可以到 Realm说明文档 进行学习(不知是网络问题还是官方没有整理好,我这边中文版文档是打不开的,所以只能看英文版),这边我们直接将里面常用到的内容整理出来,简单说下怎么使用。
-
首先,一样还是需要打开终端将 Realm 放到我们的工程中
npm install --save realm
接着,添加 Realm 与 工程的链接
- React-Native >= 0.31.0
react-native link realm
- React-Native < 0.31.0
rnpm link realm
- 出现上面的提示表示成功,然后我们需要卸载模拟器中已经安装的
APP
并重新安装(Xcode会进行一系列配置,其中会在网络下载一下必要的组件,时间视网络情况而定),来测试下安卓和iOS,2端是否能正常使用
如果出现有 err!
等字样或者在安卓中出现错误警告,说明安卓端没有成功地进行全部配置,需要我们手动进行配置,步骤如下:
-
如果出现
android Missing Realm constructor - please ensure RealmReact framework is included
报错:- 在
MainApplication
中添加
- 在
new RealmReactPackage()
如果还是链接不上,我们检查以下几处代码是否有自动添加
- settings.gradle 中是否有下面代码,不存在手动添加
include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
- 如果还不行,到app => build.gradle 中是否有下面代码,不存在手动添加
dependencies { compile project(':realm') // 是否存在,不存在手动添加(再旧版本有效,新版本不需要添加此项) compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules }
接着,重新运行安卓:
react-native run-android
- 如果还是不行,可联系官方,或者将错误代码发送给我,也许可以帮忙解决。
Realm 常用操作
- 作为数据库,使用它无法就是
增删改查
这老四样,使用之前,还是老规矩,初始化表格:- name:表格名称。
- primaryKey:主键,这个属性的类型可以是 'int' 和 'string',并且如果设置主键之后,在更新和设置值的时候这个值必须保持唯一性,并且无法修改。
- properties:这个属性内放置我们需要的字段。
// 新建表模型 const PersonSchema = { name: 'Person', primaryKey:'id', // 官方没给出自增长的办法,而且一般不会用到主键,这也解决了重复访问的问题,而且实际开发中我们不需要主键的,让服务端管就是了 properties: { id:'int', name: 'string', tel_number: {type: 'string', default: '156xxxxxxxx'}, // 添加默认值的写法 city: 'string' // 直接赋值的方式设置类型 } };
- 初始化 Realm:
// 根据提供的表初始化 Realm,可同时往数组中放入多个表 let realm = new Realm({schema: [PersonSchema]});
- 增加数据:
// 增加 createData() { realm.write(() => { realm.create('Person', {id:0, name:'吉泽明步', tel_number:'137xxxxxxxx', city:'xx省xx市xxxxxx'}); realm.create('Person', {id:1, name:'苍井空', tel_number:'137xxxxxxxx', city:'xx省xx市xxxxxx'}); realm.create('Person', {id:2, name:'小泽玛利亚', tel_number:'137xxxxxxxx', city:'xx省xx市xxxxxx'}); realm.create('Person', {id:3, name:'皮皮虾我们走', tel_number:'137xxxxxxxx', city:'xx省xx市xxxxxx'}); realm.create('Person', {id:4, name:'波多野结衣', tel_number:'137xxxxxxxx', city:'xx省xx市xxxxxx'}); }) }
查询数据
- 查询所有数据:
// 查询所有数据 let persons = realm.objects('Person'); console.log ('name:' + persons[0].name + 'city:' + persons[0].city)
- 根据条件查询数据
// 查询 inquireData() { let allData; // 获取Person对象 let Persons = realm.objects('Person'); // 遍历表中所有数据 for (let i = 0; i<Persons.length; i++) { let tempData = '第' + i + '个' + Persons[i].name + Persons[i].tel_number + Persons[i].city + ' '; allData += tempData } this.setState({ data:allData }) } // 根据条件查询 filteredData() { let allData; // 获取Person对象 let Persons = realm.objects('Person'); // 设置筛选条件 let person = Persons.filtered('id == 1'); if (person) { // 遍历表中所有数据 for (let i = 0; i<person.length; i++) { let tempData = '第' + (person[i].id + 1) + '个数据:' + person[i].name + person[i].tel_number + person[i].city + ' '; allData += tempData } } this.setState({ data:'筛选到的数据:' + allData }) }
更新数据:
// 更新 upData() { realm.write(() => { // 方式一 realm.create('Person', {id: 0, name: '皮皮虾,我们走', tel_number: '156xxxxxxxx', city: 'xx省xx市xxxxxx'}, true); // // 方式二:如果表中没有主键,那么可以通过直接赋值更新对象 // // 获取Person对象 // let Persons = realm.objects('Person'); // // 设置筛选条件 // let person = Persons.filtered('name == 苍井空'); // // 更新数据 // person.name = '黄鳝门' }) }
- 删除数据:
// 删除 removeData() { realm.write(() => { // 获取Person对象 let Persons = realm.objects('Person'); // 删除 realm.delete(Persons); }) }
转载自:https://www.jianshu.com/p/78b4b4b9d041