uniapp 缓存指定接口的响应,在无网络时使用缓存数据

App.vue

复制代码
onShow: function () {
		uni.onNetworkStatusChange((res) => {
			uni.setStorageSync('networkConnected', res.isConnected);
		});
	},

common.js

复制代码
/**
* 显示消息提示框
* @param content 提示的标题
*/
export function toast(content) {
  uni.showToast({
    icon: 'none',
    title: content
  })
}

/**
* 显示模态弹窗
* @param content 提示的标题
*/
export function showConfirm(content,showCancel) {
  return new Promise((resolve, reject) => {
    uni.showModal({
      title: '提示',
      content: content,
      cancelText: '取消',
      confirmText: '确定',
	  showCancel: showCancel==null?true:showCancel,
      success: function(res) {
        resolve(res)
      }
    })
  })
}

/**
* 参数处理
* @param params 参数
*/
export function tansParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    var part = encodeURIComponent(propName) + "="
    if (value !== null && value !== "" && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']'
            var subPart = encodeURIComponent(params) + "="
            result += subPart + encodeURIComponent(value[key]) + "&"
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&"
      }
    }
  }
  return result
}

pathDataCache.js

复制代码
//设置
export function setPathAndData(pathAndParameter, data) {
	let obj = uni.getStorageSync('path_data_cache') || {};
	obj[pathAndParameter]= data;
	uni.setStorageSync('path_data_cache', obj);
}

// 获取
export function getPathAndData(pathAndParameter) {
	let obj = uni.getStorageSync('path_data_cache');
	return obj?.[pathAndParameter];
}

data.js

复制代码
// 本地静态那些接口需要缓存数据
export const localityUrlPathList = [
	'/user/api/mingmenHomePageInfo/getByType', 
	'/user/api/dict/AI_IS_OPEN'
]

request.js 这个文件实现了无网读取缓存的功能

复制代码
import store from '@/store'
import config from '@/config'
import {
	getToken,
	removeToken
} from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import {
	toast,
	showConfirm,
	tansParams
} from '@/utils/common'
import {
	setPathAndData,
	getPathAndData
} from '@/utils/pathDataCache.js'
import {
	localityUrlPathList
} from './data.js'

let timeout = 40000
const baseUrl = config.baseUrl

// 添加全局变量控制弹窗状态
let isShowingLoginModal = false

// 需要缓存接口url的数组
let cachePathList = uni.getStorageSync('setUrlPathDataList') || localityUrlPathList;

const request = async config => {
	const networkConnected = uni.getStorageSync('networkConnected');
	// console.log('cachePathList', cachePathList);
	// 是否需要设置 token
	const isToken = (config.headers || {}).isToken === false
	config.header = config.header || {}
	if (getToken() && !isToken) {
		config.header['Authorization'] = 'Bearer ' + getToken()
	}

	// get请求映射params参数.
	// console.log('config.url',config.url);
	//console.log('getUrlPath',getUrlPath(config.url));
	if (config.params) {
		let url = config.url + '?' + tansParams(config.params)
		url = url.slice(0, -1)
		config.url = url
	}

	// console.log('是否有网络',uni.getStorageSync('networkConnected'));
	if (cachePathList.includes(getUrlPath(config.url))) {
		let setDataKey = serializeRequest(config.method || 'get', config.url, config.data);
		// 包含需缓存
		if (networkConnected) {
			//有网络,请求并缓存
			try {
				let respData = await realRequest(config)
				// console.log('setDataKey', setDataKey, respData);
				setPathAndData(setDataKey, respData);
				return Promise.resolve(respData);
			} catch (error) {
				// console.log('error', error);
				return Promise.reject(error);
			}
		} else {
			//无网络,取缓存,没有则报错
			let dataa = getPathAndData(setDataKey);
			// console.log('获取数据',dataa,setDataKey);
			if (!dataa) {
				// console.log('无网络,无缓存');
				return Promise.reject('nocatch');
			}
			return Promise.resolve(dataa);
		}
	} else {
		// 不包含 正常请求
		// console.log('config',config);
		return realRequest(config);
	}


}

// 处理未授权错误
function handleUnauthorized(reject) {
	// 如果已经有弹窗显示,则不再重复显示
	if (isShowingLoginModal) {
		reject('无效的会话,或者会话已过期,请重新登录。')
		return
	}

	isShowingLoginModal = true
	showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
		isShowingLoginModal = false
		if (res.confirm) {
			removeToken()
			uni.reLaunch({
				url: '/pages/login'
			})
		}
	}).catch(() => {
		isShowingLoginModal = false
	})

	reject('无效的会话,或者会话已过期,请重新登录。')
}

// 处理请求错误
function handleRequestError(error, reject) {
	let {
		message
	} = error
	if (message === 'Network Error') {
		message = '后端接口连接异常'
	} else if (message.includes('timeout')) {
		message = '系统接口请求超时'
	} else if (message.includes('Request failed with status code')) {
		message = '系统接口' + message.substr(message.length - 3) + '异常'
	}
	toast(message)
	reject(error)
}

function getUrlPath(url) {
	return url.split('?')[0];
}

/**
 * 真实请求
 * @param {Object} config
 */
function realRequest(config) {
	const networkConnected = uni.getStorageSync('networkConnected');
	return new Promise((resolve, reject) => {
		uni.request({
			method: config.method || 'get',
			timeout: config.timeout || timeout,
			url: config.baseUrl || baseUrl + config.url,
			data: config.data,
			header: config.header,
			dataType: 'json'
		}).then(response => {
			let [error, res] = response
			// console.log('response',config.method,config.url,response,error);
			// console.log('getToken()',getToken());
			if (res.data.error) {
				// console.log('500错误的地方',networkConnected,error);
				if (networkConnected) {
					toast('服务器忙')
				} else {
					uni.showToast({
						icon: 'none',
						title: '网络异常,请检查网络设置',
						duration: 2000,
					});
				}
				reject('服务器忙')
				return
			}

			const code = res.data.code || 200
			const msg = errorCode[code] || res.data.msg || errorCode['default']

			if (code === 401) {
				// 处理401未授权错误
				handleUnauthorized(reject)
			} else if (code === 500) {
				// 判断是否有网络
				if (networkConnected) {
					uni.showToast({
						icon: 'none',
						title: msg,
						duration: 2000
					});
				} else {
					uni.showToast({
						icon: 'none',
						title: '网络异常,请检查网络设置',
						duration: 2000,
					});
				}
				reject('500');
			} else if (code == 1) {
				toast(msg)
				reject(code)
			} else if (code == 2001) {
				toast(msg)
				reject(code)
			} else if (code > 8000 && code < 9000) {
				reject(code)
			} else if (code !== 200) {
				showConfirm('后端服务错误,请稍后在试', false).then(res => {
					if (res.confirm) {
						// 用户确认后的操作
					}
				})
				reject(code);
			}
			resolve(res.data);
		}).catch(error => {
			handleRequestError(error, reject)
		})
	})
}
/**
 * http请求方式,路径,参数系列化
 */
function serializeRequest(method, path, params = {}) {
	const methodUpper = method.toUpperCase();
	const queryString = Object.keys(params)
		.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
		.join('&');

	return queryString ?
		`${methodUpper}${path}?${queryString}` :
		`${methodUpper}${path}`;
}

export default request
相关推荐
We་ct3 小时前
LeetCode 97. 交错字符串:动态规划详解
前端·算法·leetcode·typescript·动态规划
Chengbei113 小时前
轻量化 Web 安全日志分析神器 星川智盾日志威胁检测、地理溯源、MITRE ATT&CK 映射,支持 Windows/macOS/Linux
前端·人工智能·安全·web安全·macos·系统安全·安全架构
风流 少年3 小时前
Python Web框架:FastAPI
前端·python·fastapi
GISer_Jing3 小时前
AI时代面试新常态——从“会用工具”到“深挖原理”的跨越
前端·人工智能·ai编程
IT_陈寒3 小时前
React的useEffect把我坑惨了,这些闭包陷阱真要命
前端·人工智能·后端
前端之虎陈随易3 小时前
有生之年系列,Nodejs进程管理pm2 v7.0发布
前端·typescript·npm·node.js
ayqy贾杰4 小时前
Cursor SDK发布!开发者可直接搬走其内核
前端·vue.js·面试
椰猫子4 小时前
SpringMVC(SpringMVC简介、请求与响应(请求映射路径、请求参数、日期类型参数传递、响应json数据))
java·前端·数据库
love530love4 小时前
如何在 Google Chrome 中强制开启 Gemini AI 侧边栏(完整图文教程)
前端·人工智能·chrome·windows
光影少年4 小时前
对typescript开发框架的理解?
前端·javascript·typescript