分享到:
发表于 2025-06-14 22:10:52 楼主 | |
前情uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE可视化的运行和打包也让开发体验也非常棒,公司项目就是主推uni-app,为了用户体验对于耗时操作,如接口请求或者异步的API调用都会添加loading效果 原生API使用持性uni-app项目本身自带有原生交互反馈的API,如showToast、showLoading、showModal、showActionSheet,各有应用场景,而其中showLoading就是用于显示一个加载中效果的,同时跟他配套的还有hideLoading,用于隐藏已经显示的loading效果 showLoading特性:永远只会有一个,如果同时调用多次只会显示最后调用的那一个 hideLoading特性:调用它会关掉页面上的正在显示的loading 思考:
假设一个场景,我页面上同时调起二个接口,一个接口都会调起一个loading ,最后显示的是后调接口的loading,但是此时前一个接口回来了,会调用hideLoading接口,就会隐藏loading,其实第二个接口还没有回来,导致loading被关了,测试代码如下: scss代码解读复制代码https://www.ysdslt.com/const test0 = () => { uni.showLoading({ title: '加载中...0', mask: true }); setTimeout(() => { uni.hideLoading(); }, 1000); } const test1 = () => { uni.showLoading({ title: '加载中...1', mask: true }); setTimeout(() => { uni.hideLoading(); }, 2000); } test0(); test1();
在我们做接口请求的时候,接口速度是会受用户的网速影响,网速好就接口返回快,网络差就接口返回慢,这里会有一个问题,如果用户网络好那loading会闪一下就没了,用户都没看到是什么东西,在一定层面上给用户带来不好的体验 解决方案
因hideLoading并不会识别当前显示是由谁唤起的loading,导致无法识别当前要隐藏的是哪一个Loading,那我们就封装下代码,记录已经唤起的loading,在调用loading的时候通过传参指定要隐藏的是哪一个 loading就行了
我们无法确认用户的网络状态,那我们在唤起loading的时候是否可以做一个延时显示,假设我们延时为300ms,如果发现300ms内有唤起当前loading的hideloading调用,那当前loading也就不用显示了,但是这又有一个问题,如果网络正好回复时间了310,那一样是闪一下,所以我们再加一个loading的最小显示时间 uni-app项目一想到要全局操作,首选第一想到就是事件通信了,此处我们基于uni-app自带的事件通信on、emit来实现一个能解决上面缺陷的loading显示与隐藏方案,完整代码如下: ini代码解读复制代码// 用于存储记录当前已有https://www.4922449.com/loading let loadingObj = {}; // loading需要显示的基准时间 let loadingDelayShow = 300; // loading显示的最小时间 let loadingDelayHide = 1000; let loadingTimer = {}; // 默认 loading 配置 const loadingOptionsDefault = { mask: true } /** * 初始化loading * 基于事件通知实现 loading 的显示与隐藏 */ export const initLoading = (options = '数据加载中...') => { // 监听显示 loading 事件 uni.$off('showLoading'); // optionsIn是loading的配置,参考showLoading的配置, // 其中key用于存储当前是什么哪一个loading,用于与hideLoading配合使用 uni.$on('showLoading', (optionsIn = options, key) => { console.log('---- showLoading ----:', optionsIn, key); // 如果传入的是字符串,则将其作为 title const loadingOptions = typeof optionsIn === 'string' ? { ...loadingOptionsDefault, title: optionsIn } : { ...loadingOptionsDefault, ...optionsIn }; if (!loadingObj[key]) { loadingObj[key] = { show: false, startTime: Date.now() }; } // 如果300ms内又调用了hideloading,则无需显示loading loadingTimer[key] = setTimeout(() => { if (loadingObj[key] && !loadingObj[key].show) { loadingObj[key].show = true; loadingObj[key].startTime = Date.now(); uni.showLoading({...loadingOptions}); } }, loadingDelayShow); }); // 监听隐藏 loading 事件 uni.$off('hideLoading'); uni.$on('hideLoading', (key) => { if (loadingObj[key]) { loadingObj[key].show = false; clearTimeout(loadingTimer[key]); } if (isCanHide(key)) { const { startTime } = loadingObj[key]; if (Date.now() - startTime >= loadingDelayHide) { resetLoading(); } else { setTimeout(() => { if (isCanHide(key)) { resetLoading(); } }, loadingDelayHide - (Date.now() - startTime)); } } }); // 监听重置 loading 事件 uni.$off('resetLoading'); uni.$on('resetLoading', () => { try { resetLoading(); } catch (err){ console.log('---- resetLoading ----', err); } }); } // 判断要不要隐藏loading const isCanHide = (key) => { if (!loadingObj[key].show && Object.keys(loadingObj).length === 1) { return true; } // 或者所有的loading对象的show都是false for (const key in loadingObj) { if (loadingObj[key].show) { return false; } } return true; } const resetLoading = () => { loadingObj = {} uni.hideLoading(); } // 导出事件名称常量 export const LOADING_EVENTS = { SHOW: 'showLoading', HIDE: 'hideLoading', RESET: 'resetLoading' }; // 导出默认对象,包含事件名称常量 export default { LOADING_EVENTS }; 使用方式: 在项目根目录main.js中初始化 j代码解读复制代码import { initLoading } from '@/utils/loading'; initLoading() 在需要的地方调用 php代码解读复制代码// 显示loading uni.$emit('showLoading', '提交订单中...', 'xxx'); // 隐藏loading uni.$emit('hideLoading', 'xxx'); 使用注意事项
小结这是我的 uni-app项目的loading使用方案,已使用在生产中,还没有发现什么问题,如果后续发现什么问题再做更新吧,解决方案千千万,世上没有最好,只有更好,如果你有更好的解决方案,可以分享出来,或者你发现此方案有什么问题,也可以提出来一起探讨。 |
|
楼主热贴
个性签名:无
|
针对ZOL星空(中国)您有任何使用问题和建议 您可以 联系星空(中国)管理员 、 查看帮助 或 给我提意见