写点什么

ReactNative 进阶(十二):本地存储 AsyncStorage 及 Realm 使用

  • 2022 年 1 月 12 日
  • 本文字数:4507 字

    阅读完需:约 15 分钟

ReactNative进阶(十二):本地存储 AsyncStorage 及 Realm 使用

一、前言

数据持久化一直都是软件开发中重要的一个环节,几乎所有的应用都具备这项功能;什么是数据持久化呢?说白了就是数据的本地化存储,将数据存储到本地,在需要的时候进行调用。


本文介绍两种在 React-Native 中比较常用的存储方式:AsyncStorageRealm


  • AsyncStorage:官方使用的存储方式,类似于 iOS 中的 NSUserDefault ,区别在于,AsyncStorage 只能存储字符串键值对,而 NSUserDefault 可以存储字符串number

  • Realm:新兴的移动端数据存储方式,在它未出现之前,移动端一直都是使用 sqlist 进行数据存储,在性能上,各有优势,但是操作上,Realm 有明显优势,更方便使用。


接下来我们就来看看怎么使用它们。

二、AsyncStorage

AsyncStorage方法使用官方文档写得很详细,这边就不对赘述了!


示例代码如下:


// 增加createData() {   AsyncStorage.setItem('name', JSON.stringify('Hello RN!'), (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('Hello Vue'), (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:'Vue',          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:'Angular', city:'xx省xxx市' },
// 设为null,则不过期,这里会覆盖初始化的时效 expires: 1000 * 3600 });}
// 删除removeData() { // 删除单个数据 storage.remove({ key: 'storageTest' });
// storage.remove({ // key: 'react-native-storage-test', // name:'Vue' // });
// // !! 清空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');}
复制代码

三、Realm

首先,将 Realm 引入工程中。


npm install --save realm
复制代码


接着,添加 Realm 与工程的链接


// React-Native >= 0.31.0
react-native link realm// React-Native < 0.31.0
rnpm link realm
复制代码

3.1 Realm 常用操作

作为数据库,使用它无非就是 CRUD 操作,使用之前,初始化表格:


  • 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 + '\n'; 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 + '\n'; 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 == Vue');   // 更新数据   // person.name = '黄鳝门' })}
复制代码


删除数据


// 删除removeData() {   realm.write(() => {       // 获取Person对象       let Persons = realm.objects('Person');       // 删除       realm.delete(Persons);   })}
复制代码

四、拓展阅读

发布于: 刚刚阅读数: 2
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
ReactNative进阶(十二):本地存储 AsyncStorage 及 Realm 使用