react-native 使用AsyncStorage实现长登陆

像京东,淘宝等app登陆一次在账户未出现问题(如密码修改,账户异常等)情况下再次打开客户端都不需要登陆,为了实现类似原生app的长登陆,今天就分享下在react-native中如何实现这一逻辑:

AsyncSotrage 是官方提供的异步存储,类似安卓的sp(sharePreference)和ios的NSUserDefaults,适合存储用户的账户信息,登陆信息等,官方介绍点这里,

当然,文档中也说了 react-native中文网维护了一个封装后的react-native-storage模块,使用更方便,避免重复造轮子,用起来,

首先,需要对storage进行初始化 

/**
 * Created by lubowen on 2017/3/8.
 */
import { AsyncStorage } from 'react-native';
import Storage from 'react-native-storage';
// import NetTool from '../common/NetTool'
import {HOST} from '../common/NetAPI'
//存储token

var storage = new Storage({
    // 最大容量,默认值1000条数据循环存储
    size: 1000,

    // 存储引擎:对于RN使用AsyncStorage,对于web使用window.localStorage
    // 如果不指定则数据只会保存在内存中,重启后即丢失
    storageBackend: AsyncStorage,

    // 数据过期时间,默认一整天(1000 * 3600 * 24 毫秒),设为null则永不过期
    defaultExpires: 500 * 3600, //有效期半个小时500 * 3600

    // 读写时在内存中缓存数据。默认启用。
    enableCache: true,

    // 如果storage中没有相应数据,或数据已过期,
    // 则会调用相应的sync方法,无缝返回最新数据。
    // sync方法的具体说明会在后文提到
    // 你可以在构造函数这里就写好sync的方法
    // 或是写到另一个文件里,这里require引入
    // 或是在任何时候,直接对storage.sync进行赋值修改
    sync: {
        // sync方法的名字必须和所存数据的key完全相同
        // 方法接受的参数为一整个object,所有参数从object中解构取出
        // 这里可以使用promise。或是使用普通回调函数,但需要调用resolve或reject。
        async loginState(params){
            var code;
            //console.log(params)
                let { id, resolve, reject,syncParams: { extraFetchOptions, someFlag } } = params;
            if (someFlag) {
                code=extraFetchOptions.code
            }
                fetch(HOST+'loginapi/getToken?code='+code, {
                    method: 'GET',
                }).then(response => {
                    return response.json();
                }).then(json => {
                    console.log(json)
                    if(json && json.token){
                        storage.save({
                            key: 'loginState',
                            rawData: json.token,
                            expires:3600*500
                        });
                        console.log("换token啦")
                        // 成功则调用resolve
                        resolve && resolve(json.token);
                    }
                    else{
                        //失败则调用reject
                        reject && reject(new Error('data parse error'));
                    }
                }).catch(err => {
                    console.warn(err);
                    //reject && reject(err);
                });
        }
    }
})

// 最好在全局范围内创建一个(且只有一个)storage实例,方便直接调用

// 对于web
// window.storage = storage;

// 对于react native
    global.storage = storage;

// 这样,在此**之后**的任意位置即可以直接调用storage
// 注意:全局变量一定是先声明,后使用
// 如果你在某处调用storage报错未定义
// 请检查global.storage = storage语句是否确实已经执行过了




其中sync字段返回的是一个重新获取token的方法,这个视个人项目来灵活变动

并在程序的统一入口 把storage变量引入,使其成为全局变量方便调用,

当用户登录完成后,保存token,

 storage.save({ key: 'loginState', rawData:token, expires:3600*500});     //单独保存token  半小时
 storage.save({ key: 'code', rawData:code,expires:null});     //单独保存code



并定义token的存储时间为半个小时,接下来在我们需要网络请求,并带上token访问的时候,将token加到请求头,下面是封装的网络请求方法,

 static  async get(url,params){
        var token ;
        var url=HOST+url   //拼接url
        //读取code
        var code;
        await storage.load({
            key: 'code',
            autoSync: false,
            syncInBackground: true,
        }).then(ret => {
            // console.log(ret);   //code
            code=ret;
        }).catch(err => {
            //如果没有找到数据且没有sync方法,
            //或者有其他异常,则在catch中返回
            //console.warn(err.message);
            switch (err.name) {
                case 'NotFoundError':
                    console.log("没找到code")
                    break;
                case 'ExpiredError':
                    console.log("code读取错误")
                    break;
            }
        })

        // 读取token
        if(typeof code==="string"){
            await storage.load({
                key: 'loginState',
                // autoSync(默认为true)意味着在没有找到数据或数据过期时自动调用相应的sync方法
                autoSync: true,
                // syncInBackground(默认为true)意味着如果数据过期,
                // 在调用sync方法的同时先返回已经过期的数据。
                // 设置为false的话,则始终强制返回sync方法提供的最新数据(当然会需要更多等待时间)。
                syncInBackground: false,
                // 你还可以给sync方法传递额外的参数
                syncParams: {
                    extraFetchOptions: {
                        code:code       //如果code存在,传过去拿token
                    },
                    someFlag: true, //传一些参数 code
                },
            }).then(ret => {
                // 注意:这是异步返回的结果(不了解异步请自行搜索学习)
                // 你只能在then这个方法内继续处理ret数据
                // 而不能在then以外处理
                // 也没有办法“变成”同步返回
                // 你也可以使用“看似”同步的async/await语法
                token=ret;
            }).catch(err => {
                //如果没有找到数据且没有sync方法,
                //或者有其他异常,则在catch中返回
                // console.warn(err.message);
                switch (err.name) {
                    case 'NotFoundError':
                        console.log("哈哈")
                        break;
                    case 'ExpiredError':
                        console.log("哈哈2")
                        break;
                }
            })
        }

        if (params) {
            let paramsArray = [];
            if(typeof token === "string"){ //如果有token
                paramsArray.push('token'+ '=' + token) //添加token
            }
            //拼接参数
            Object.keys(params).forEach(key => paramsArray.push(key + '=' + params[key]))
            if (url.search(/\?/) === -1) {
                url += '?' + paramsArray.join('&')
            } else {
                url += '&' + paramsArray.join('&')
            }
        }
        console.log(url)
        return new Promise((resolve, reject) => {
              fetch(url, {
                  method:'GET',
              }).then((response) => {
                  result = response.json();
                  resolve(result);
              }).catch((error) => {
                  reject(error);
              });
          });

    }


当token过期时,会自动调用storage.sync.xxx.  其中xxx为存储的key值,然后再xxx方法中,会通过获取token的接口获取最新的 token,并重新存储起来,然后再拿到最新的token,访问网络请求数据。至此就基本做到了保持登陆状态。当然因个人项目而异,但方法基本相同。






你可能感兴趣的:(存储,登陆,react-native)