React-Native 之 数据持久化

数据持久化

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

  • 这边咱们介绍两种在 React-Native 中比较经常使用的存储方式node

    • AsyncStorage:这是官方使用的存储方式,相似于 iOS 中的 NSUserDefault ,区别在于,AsyncStorage 只能存储 字符串键值对,而 NSUserDefault 能够存储 字符串和number
    • Realm:今天才发现 Realm 也已经支持 React-Native ,这是新兴的移动端数据存储方式,在没有它以前,一直都是使用 sqlist 进行数据存储,在性能上,各有优点,可是操做上,Realm 有着明显优点,更方便使用。
  • 接下来咱们就来看看怎么使用它们。react

AsyncStorage 简单使用

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

  • AsyncStorage 使用方法很简单,咱们就直接上代码:ios

// 增长
    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,咱们能够直接使用它,方法很简单,说明文档中说得很详细。git

  • 既然是第三方框架,那么第一部确定就是导入到咱们的工程中:github


 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 文件确定是最早调用的,它们才是真正的王)。web

  • 然而,为了方便咱们使用同一套代码,咱们会建立一个 Main 文件做为程序入口的 中转总站 来管理其余的文件,而后外界只要调用这个 Main 文件,就能够展现里面的全部东西。因此,将引用放到 Main 文件中是最好的选择。sql

    
    // 在 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');
    }

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 + '\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 == 苍井空');
            // // 更新数据
            // person.name = '黄鳝门'

        })
    }
  • 删除数据:
// 删除
    removeData() {
        realm.write(() => {
            // 获取Person对象
            let Persons = realm.objects('Person');
            // 删除
            realm.delete(Persons);
        })
    }

转载自:https://www.jianshu.com/p/78b4b4b9d041

相关文章
相关标签/搜索