像京东,淘宝等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,访问网络请求数据。至此就基本做到了保持登陆状态。当然因个人项目而异,但方法基本相同。