zoukankan      html  css  js  c++  java
  • React-Native 之 数据持久化

    数据持久化

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

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

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

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

    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

  • 相关阅读:
    【LeetCode OJ】Remove Element
    【LeetCode OJ】Remove Duplicates from Sorted Array
    【LeetCode OJ】Swap Nodes in Pairs
    【LeetCode OJ】Merge Two Sorted Lists
    【LeetCode OJ】Remove Nth Node From End of List
    【LeetCode OJ】Two Sum
    【LeetCode OJ】Majority Element
    最长公共子序列问题
    php fopen与file_get_contents的区别
    PHP 技巧集合
  • 原文地址:https://www.cnblogs.com/qiyecao/p/8559423.html
Copyright © 2011-2022 走看看