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

相關文章
相關標籤/搜索