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
相关推荐
GIS开发特训营2 小时前
2025年华中农业大学暑期实训优秀作品(4):智慧煤仓监控系统平台——重塑煤炭仓储管理新模式
前端·vue.js·信息可视化
奺儿2 小时前
uniapp 小程序 报错 TypeError: Cannot convert undefined or null to object
小程序·uni-app
腾讯云云开发2 小时前
【CloudBase MCP 升级福利】你的AI开发搭子已进化!晒出AI Coding项目领取周边礼品
前端·后端·小程序·云开发
Web极客码2 小时前
如何在WordPress网站中添加Cookie弹窗
前端·安全·github·wordpress
这儿有一堆花2 小时前
从 Markdown 到 HTML 的正确构建路径
前端·html
2501_915921432 小时前
从 HBuilder 到 App Store,uni-app 与 HBuilder 项目的 iOS 上架流程实战解析
android·ios·小程序·https·uni-app·iphone·webview
LeeAt2 小时前
手搓一个 Ollama 本地 SSE 全栈聊天助手
前端
全_3 小时前
全栈项目实践五:抽离npm包
前端
dorisrv3 小时前
使用requestIdleCallback和requestAnimationFrame优化前端性能
前端