react-native 网络请求卡顿,async-storage本地存储慢的问题

探讨了在React Native项目中优化网络请求速度的方法,特别是针对本地存储导致的延迟问题。介绍了使用原生存储替代AsyncStorage,以及通过数据缓存减少网络请求时间的具体实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当项目中在http网络请求基类中需要进行本地存储时,可能会导致网络请求慢,需要特别的注意.

import {toastShort} from './ToastUtil';
import fetch from './fetch-polyfill';
import {NativeModules} from 'react-native';
import SceneUtils from './SceneUtils';
import LoadingStore from '../component/loading/LoadingStore';
import strings from "../common/strings";
import appsFlyer from 'react-native-appsflyer';
import * as AesUtil from "./AesUtil";
import GlobalStore from "../page/GlobalStore";
import _ from 'lodash';

const {HttpConfig, Config} = NativeModules;

/**
 * 网络请求
 * @param url
 * @param method
 * @param formData
 * @param loadingType 全局添加等待状态,仅用于单个http网络请求等待标识
 *        loadingType=0 使用Loading等待框
 *        loadingType=1 使用LoadingView等待框
 */

/** 如果yield call的是一个Promise对象,那只有在Promise返回的是resolve方法的情况下,
 * 下面跟着的yield put及后面的代码才会执行,若返回了reject则后面的代码则全部停止执行
 * https://www.jianshu.com/p/045d6fc47de4
 */
function httpFetch(url, method, formData, loadingType?: number = -1) {
  return new Promise((resolve, reject) => {
    const headParams = {
      'Connection': 'keep-alive',
      'Content-Type': 'application/x-www-form-urlencoded', //默认格式
      'credentials': 'include', //包含cookie
      'mode': 'cors', //允许跨域
    };
    Config.getData(strings.SESSIONID, 0, (sessionId) => {
      Config.getData(strings.UID, 0, (uid) => {
        console.log(url, sessionId + ":" + uid);
        if (sessionId && uid) {
          headParams.Cookie = `SESSIONID=${sessionId};UID=${uid}`;
        }
        if (loadingType === 0) {
          LoadingStore.getInstance().setLoading(true);
        } else if (loadingType === 1) {
          LoadingStore.getInstance().setLoadingView(true);
        }
        Config.getData(strings.usePrepareUrl, 1, usePrepareUrl => {
          let prepardUrl = null;
          if (usePrepareUrl) {
            prepardUrl = strings.product_base_url_prepared;
          }
          requestData(prepardUrl, url, method, formData, headParams, loadingType).then(resultObject => {
            resolve(resultObject);
          }).catch(err => {
            if (loadingType === 0) {
              LoadingStore.getInstance().setLoading(false);
            } else if (loadingType === 1) {
              LoadingStore.getInstance().setLoadingView(false);
            }
            reject(err);
          });
        });
      });
    });
  });
}

function requestData(prepareUrl? = null, url, method, formData, headParams, loadingType?: number = -1) {
  let debugMode = HttpConfig.isDebug && GlobalStore.getInstance().jsDebug;
  let baseUrl = null;
  if (!prepareUrl) {
    baseUrl = debugMode ? strings.test_base_url : strings.product_base_url;
  } else {
    baseUrl = prepareUrl;
  }
  console.log("baseUrl", baseUrl);
  console.log("url", url);
  let fullUrl = baseUrl + url;
  let paramsArray = [];
  //formData添加通用参数
  formData.clientType = 'android';
  formData.appVersion = HttpConfig.appVersion;
  formData.pkg_name = HttpConfig.pkg_name;
  formData.deviceName = HttpConfig.deviceName;
  formData.osVersion = HttpConfig.osVersion;
  formData.utmsource = HttpConfig.utmsource;
  formData.apk_source = HttpConfig.apk_source;

  return new Promise((resolve, reject) => {

    // appsFlyer.getAppsFlyerUID((error, appsFlyerUID) => {
    //   if (error) {
    //     console.error(error);
    //     formData.appsflyerId = "";
    //   } else {
    //     formData.appsflyerId = appsFlyerUID;
    //   }

      Config.getData(strings.deviceId, 0, async deviceId => {
        if (deviceId)
          formData.deviceId = deviceId;
        let source = await HttpConfig.getSource();
        formData.source = source;

        formData.advertisingId = await HttpConfig.getAdvertisingAd();
        console.log(formData);
        let encryptData = await AesUtil.encrypt(JSON.stringify(formData));

        paramsArray.push(`en_data=${encodeURIComponent(encryptData)}`);
        fetch(fullUrl, {
          method,
          headers: headParams,
          timeout: 30 * 1000,
          body: method === 'GET' ? null : paramsArray.join('&'),
        }).then(response => {
              if (response.ok) {
                return response.json();
              } else {
                if (loadingType === 0) {
                  LoadingStore.getInstance().setLoading(false);
                } else if (loadingType === 1) {
                  LoadingStore.getInstance().setLoadingView(false);
                }

                reject({
                  code: -1,
                  message: response.statusText,
                });
              }
            })
            .then(async encryptJson => {
              if (loadingType === 0) {
                LoadingStore.getInstance().setLoading(false);
              } else if (loadingType === 1) {
                LoadingStore.getInstance().setLoadingView(false);
              }
              let code = -1;
              let message = "Kesalahan parsing data";
              console.log(url, encryptJson);
              if (encryptJson) {
                code = parseInt(encryptJson.code);
                if (encryptJson.message)
                  message = encryptJson.message;
                if (code === 0 || code === 1001) {
                  let {data} = encryptJson;
                  if (data && _.isString(data)) {
                    let decryptData = await AesUtil.decrypt(data);
                    console.log("decryptData", decryptData);
                    if (decryptData) {
                      resolve(JSON.parse(decryptData));
                    } else {
                      resolve(decryptData)
                    }
                  } else {
                    resolve(data)
                  }
                } else {
                  if (encryptJson.code === -2) {//重新登录
                    Config.setBoolData(strings.isLogin, false);
                    Config.remove(strings.userInfo);
                    Config.remove(strings.UID);
                    Config.remove(strings.SESSIONID);
                    toastShort(encryptJson.message, false);
                    SceneUtils.gotoLogin();
                    return;
                  }
                  if (reject) {
                    reject(encryptJson);
                    console.log(encryptJson)
                  } else {
                    toastShort(encryptJson.message, false);
                  }
                }
              } else {
                if (reject) {
                  let encryptJson = `{\"code\":${code},\"message\":\"${message}\",\"data\":{}}`;
                  reject(JSON.parse(encryptJson));
                  console.log(encryptJson)
                } else {
                  toastShort(message, false);
                }
              }
            })
            .catch(error => {
              if (loadingType === 0) {
                LoadingStore.getInstance().setLoading(false);
              } else if (loadingType === 1) {
                LoadingStore.getInstance().setLoadingView(false);
              }
              let errMsg = error && error.toString();
              let code = -1;
              if (errMsg.indexOf('Network request failed') > 0) {
                if (debugMode) {
                  code = 20002;
                  errMsg = 'Kesalahan koneksi';
                } else {
                  Config.setBoolData(strings.usePrepareUrl, true);
                }
              }
              if (errMsg.indexOf('Unexpected end of JSON input') > 0) {
                errMsg = 'Kesalahan parsing data';
              }
              console.log("httpError", url);
              if (reject) {
                reject({
                  code,
                  message: errMsg
                });
              } else {
                toastShort(errMsg, false);
              }
            });
      });
    // });
  });
}

export {httpFetch};

其中NativeModules中Config是我用原生写的本地存储代码,通过原生与RN通信的方式透传到RN端来使用,以前使用的是@react-native-community/async-storage来进行本地存储,因为这个类中需要进行加解密的操作,以及大量的本地存储数据,原来导致了每个网络请求都特别的慢

查看是否是这个问题:

这个是另外一个项目的网络请求代码

async request(method, url, params) {
    // const userAgent = DeviceInfo.getUserAgent();
    let startNetDate = new Date().getTime()
    let net = await NetInfo.getConnectionInfo();
    console.log("NetInfo",new Date().getTime()-startNetDate);
    // console.log(net)
    let plat = 'Beibeipay/5.0';
    let type = `(${DeviceInfo.getSystemName()}; ${DeviceInfo.getDeviceName()}; ${DeviceInfo.getSystemName()} ${DeviceInfo.getSystemVersion()})`;
    // console.log(type)
    let version = `Mobile/${DeviceInfo.getSystemVersion()}`
    let appName = `Beibeipay/${DeviceInfo.getVersion()}`;
    let ptn = `Partner/${config.partner}`;
    let netType = `NetType/${net.type}`;
    let lan = `Language/zh_CN`;
    let sysInfo = `${plat} ${type} ${version} RN${appName} ${ptn} ${netType} ${lan}`;
    let startTokenDate = new Date().getTime()
    const token = await AsyncStorage.getItem('userToken');
    console.log("token-End",new Date().getTime()-startTokenDate);
    // console.log(sysInfo)
    return axios.request({
      url: url,
      baseURL: config.apiBaseUrl,
      headers: {
        // 'Content-Type': 'application/json',
        'X-Api-Key': token,
        'X-Api-Partner': config.partner,
        'User-Agent': sysInfo
      },
      timeout: 30000,
      method: method,
      data: method.toLowerCase() !== 'get' ? params : null,
      params: method.toLowerCase() === 'get' ? params : null,
      responseType: 'json',
      transformRequest: [function(data) {
        if (data instanceof FormData) {
          return data
        }
        return Qs.stringify(data, {
          arrayFormat: 'brackets'
        })
      }],
      transformResponse: [function(response) {
        if (response.status === 401 || response.status === 403 || response.code === '40401' || response.code === '40403') {
          // Toast(response.msg)
          // 跳转到登录页面 传参是为了 走到login登录时强制刷新tab里面
          NavigationModule.navigate('Login', {
            origin: 'baseRequest'
          })
        } else if (response.code) {
          Toast(response.msg)
        }
        return response
      }]
    })
  }

日志记录

可以明显的看到,使用async await后异步队列明显需要等待200ms左右.1-2个本地存储的数据看不出太大的问题,但是当基类中出现加解密,以及需要获取到多个本地数据来配置参数时,就会出现大的问题

解决方案:

1.使用原生android SharedPreference类来操作本地存储的数据,已亲自验证,速度比AsyncStorage 和LocalStorage快很多,代码见最上面分享,若需要本地android 原生通信相关代码,可留言我.

2.进行数据缓存,具体是直接从内存中读取,在首次进来时,将所需代码一次性读取到内存中,后续同步修改,删除.代码如下(具体看token获取部分):

async request(method, url, params, configType? = 'v1/') {
    // const userAgent = DeviceInfo.getUserAgent();
    let net = await NetInfo.getConnectionInfo();
    // console.log(net)
    let plat = 'Beibeipay/5.0';
    let type = `(${DeviceInfo.getSystemName()}; ${DeviceInfo.getDeviceName()}; ${DeviceInfo.getSystemName()} ${DeviceInfo.getSystemVersion()})`;
    // console.log(type)
    let version = `Mobile/${DeviceInfo.getSystemVersion()}`
    let appName = `Beibeipay/${DeviceInfo.getVersion()}`;
    let ptn = `Partner/${config.partner}`;
    let netType = `NetType/${net.type}`;
    let lan = `Language/zh_CN`;
    let sysInfo = `${plat} ${type} ${version} RN${appName} ${ptn} ${netType} ${lan}`;
    // const token = await AsyncStorage.getItem('userToken');
    const token = ConfigData.getInstance().getUserToken();
    // console.log(sysInfo)
    return axios.request({
      url: url,
      baseURL: config.apiBaseUrl + configType,
      headers: {
        // 'Content-Type': 'application/json',
        'X-Api-Key': token,
        'X-Api-Partner': config.partner,
        'User-Agent': sysInfo
      },
      timeout: 30000,
      method: method,
      data: method.toLowerCase() !== 'get' ? params : null,
      params: method.toLowerCase() === 'get' ? params : null,
      responseType: 'json',
      transformRequest: [function(data) {
        if (data instanceof FormData) {
          return data
        }
        return Qs.stringify(data, {
          arrayFormat: 'brackets'
        })
      }],
      transformResponse: [function(response) {
        if (response.status === 401 || response.status === 403 || response.code === '40401' || response.code === '40403') {
          // Toast(response.msg)
          // 跳转到登录页面 传参是为了 走到login登录时强制刷新tab里面
          NavigationModule.navigate('Login', {
            origin: 'baseRequest'
          })
        } else if (response.code) {
          Toast(response.msg)
        }
        return response
      }]
    })
  }

ConfigData类:

import React, {Component} from 'react';
import AsyncStorage from '@react-native-community/async-storage';

export default class ConfigData {
  static instance;
  userToken = "";

  constructor(){

  }

  static getInstance = () => {
    if(!ConfigData.instance) {
      ConfigData.instance = new ConfigData();
    }
    return ConfigData.instance;
  };


  setUserToken(userToken,saveToDisk? = true){
    this.userToken = userToken;
    if(saveToDisk) {
      AsyncStorage.setItem('userToken', userToken);
    }
  }

  getUserToken() {
    return this.userToken;
  }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值