React Native 之 數據持久化

来源:http://www.cnblogs.com/miaomiaoshen/archive/2017/03/25/6617137.html
-Advertisement-
Play Games

前言 因為 "實戰項目系列" 涉及到數據持久化,這邊就來補充一下。 如本文有錯或理解偏差歡迎聯繫我,會儘快改正更新! 如有什麼問題,也可直接通過郵箱 [email protected] 聯繫我。 demo鏈接: https://pan.baidu.com/s/1hsspiio 密碼: dk3h 數據持 ...


前言


  • 因為 實戰項目系列 涉及到數據持久化,這邊就來補充一下。
  • 如本文有錯或理解偏差歡迎聯繫我,會儘快改正更新!
  • 如有什麼問題,也可直接通過郵箱 [email protected] 聯繫我。

數據持久化


  • 數據持久化一直都是軟體開發中重要的一個環節,幾乎所有的應用都具備這一項功能;那什麼是數據持久化呢?—— 說白了就是數據的本地化存儲,將數據存儲到本地,在需要的時候進行調用。

  • 這邊我們介紹兩種在 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效果演示.gif

  • 按照官方推薦,我們使用 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效果演示.gif

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
    

配置成功.png

  • 出現上面的提示表示成功,然後我們需要卸載模擬器中已經安裝的 APP 並重新安裝(Xcode會進行一系列配置,其中會在網路下載一下必要的組件,時間視網路情況而定),來測試下安卓和iOS,2端是否能正常使用

Xcode配置.png

  • 如果出現有 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);
        })
    }

realm效果演示.gif


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上一章我們完善了服務層的設計,傳送門:項目架構開發:服務層(下) 這次我們來完成項目的單機部署與集群部署,我們來看看單機部署與登錄 單機部署很簡單,這裡就不演示了,要註意的是我們用的是session來保存登錄信息 雖然Session不安全,比如sessionid被截獲那就可以在任何地方用你的賬號登錄 ...
  • 1、XML佈局引入 2、設置數據源數據,也就是每個item的對應文本數據 3、設置監聽,用於交互點擊和長按的事件 4、開始排序和結束排序的介面 未完善的自定義功能 1、現在僅僅是支持String,並且佈局也無法自定義,後續可能會完善Tab的item的View的自定義輸入 2、現在佈局的行數和間距由硬 ...
  • BadgeView是第三方的插件,用來顯示組件上面的標記,起到提醒的作用,下載地址如下:http://files.cnblogs.com/files/hyyweb/android-viewbadger.zip 如示意圖: 首先導入BadgeView的jar包到libs文件夾下,然後就可以使用它提供的 ...
  • 在Android原生的TextView的基礎上,可收縮/擴展的TextView:PhilExpandableTextView。 實現原理:核心是控制TextView的max lines。在TextView的初始化階段但尚未繪製出View的時候,使用ViewTreeObserver,監聽onPreDr ...
  • 在Android系統中,BroadcastReceiver的設計初衷就是從全局考慮的,可以方便應用程式和系統、應用程式之間、應用程式內的通信,所以對單個應用程式而言BroadcastReceiver是存在安全性問題的,相應問題及解決如下: 1、當應用程式發送某個廣播時系統會將發送的Intent與系統 ...
  • 1.Android 有自帶的jar包可以生成二維碼core-3.0.0.jar,其中的com.google.zxing包 2.寫一個二維碼生成的工具類,網上搜的話應該一大堆。 1 package com.example.administrator.twocodedemo; 2 3 import an ...
  • 作者:Antonio Leiva 時間:Mar 21, 2017 原文鏈接:https://antonioleiva.com/operator-overload-kotlin/ 就像其他每種語言一樣,在Kotlin中,已經預定義了一些操作符執行一定的操作。 最典型的是加(+),減(-),乘(*),除 ...
  • UINavigationController 返回手勢與 leftBarButtonItem UINavigationController 自帶從屏幕左側邊緣向右滑動的返回手勢,可以通過這個手勢實現 pop,或者 pop 中途取消 pop 而停留在當前控制器(UIViewController)。如果 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...