找回密码
 立即注册
首页 业界区 业界 uni-app项目loading显示方案

uni-app项目loading显示方案

采序 2025-6-14 17:21:32
前情

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被关了,测试代码如下:
  1. const test0 = () => {
  2.   uni.showLoading({
  3.     title: '加载中...0',
  4.     mask: true
  5.   });
  6.   setTimeout(() => {
  7.     uni.hideLoading();
  8.   }, 1000);
  9. }
  10. const test1 = () => {
  11.   uni.showLoading({
  12.     title: '加载中...1',
  13.     mask: true
  14.   });
  15.   setTimeout(() => {
  16.     uni.hideLoading();
  17.   }, 2000);
  18. }
  19. test0();
  20. test1();
复制代码

  • 使用体验问题
在我们做接口请求的时候,接口速度是会受用户的网速影响,网速好就接口返回快,网络差就接口返回慢,这里会有一个问题,如果用户网络好那loading会闪一下就没了,用户都没看到是什么东西,在一定层面上给用户带来不好的体验
解决方案


  • 解决因特性引起的使用问题
因hideLoading并不会识别当前显示是由谁唤起的loading,导致无法识别当前要隐藏的是哪一个Loading,那我们就封装下代码,记录已经唤起的loading,在调用loading的时候通过传参指定要隐藏的是哪一个 loading就行了

  • 优化体验问题
我们无法确认用户的网络状态,那我们在唤起loading的时候是否可以做一个延时显示,假设我们延时为300ms,如果发现300ms内有唤起当前loading的hideloading调用,那当前loading也就不用显示了,但是这又有一个问题,如果网络正好回复时间了310,那一样是闪一下,所以我们再加一个loading的最小显示时间
uni-app项目一想到要全局操作,首选第一想到就是事件通信了,此处我们基于uni-app自带的事件通信$on、$emit来实现一个能解决上面缺陷的loading显示与隐藏方案,完整代码如下:
  1. //  用于存储记录当前已有loading
  2. let loadingObj = {};
  3. // loading需要显示的基准时间
  4. let loadingDelayShow = 300;
  5. // loading显示的最小时间
  6. let loadingDelayHide = 1000;
  7. let loadingTimer = {};
  8. // 默认 loading 配置
  9. const loadingOptionsDefault = {
  10.   mask: true
  11. }
  12. /**
  13. * 初始化loading
  14. * 基于事件通知实现 loading 的显示与隐藏
  15. */
  16. export const initLoading = (options = '数据加载中...') => {
  17.   // 监听显示 loading 事件
  18.   uni.$off('showLoading');
  19.   // optionsIn是loading的配置,参考showLoading的配置,
  20.   // 其中key用于存储当前是什么哪一个loading,用于与hideLoading配合使用
  21.   uni.$on('showLoading', (optionsIn = options, key) => {
  22.     console.log('---- showLoading ----:', optionsIn, key);
  23.     // 如果传入的是字符串,则将其作为 title
  24.     const loadingOptions = typeof optionsIn === 'string'
  25.       ? { ...loadingOptionsDefault, title: optionsIn }
  26.       : { ...loadingOptionsDefault, ...optionsIn };
  27.     if (!loadingObj[key]) {
  28.       loadingObj[key] = {
  29.         show: false,
  30.         startTime: Date.now()
  31.       };
  32.     }
  33.     // 如果300ms内又调用了hideloading,则无需显示loading
  34.     loadingTimer[key] = setTimeout(() => {
  35.       if (loadingObj[key] && !loadingObj[key].show) {
  36.         loadingObj[key].show = true;
  37.         loadingObj[key].startTime = Date.now();
  38.         uni.showLoading({...loadingOptions});
  39.       }
  40.     }, loadingDelayShow);
  41.   });
  42.   // 监听隐藏 loading 事件
  43.   uni.$off('hideLoading');
  44.   uni.$on('hideLoading', (key) => {
  45.     if (loadingObj[key]) {
  46.       loadingObj[key].show = false;
  47.       clearTimeout(loadingTimer[key]);
  48.     }
  49.     if (isCanHide(key)) {
  50.       const { startTime } = loadingObj[key];
  51.       if (Date.now() - startTime >= loadingDelayHide) {
  52.         resetLoading();
  53.       } else {
  54.         setTimeout(() => {
  55.           if (isCanHide(key)) {
  56.             resetLoading();
  57.           }
  58.         }, loadingDelayHide - (Date.now() - startTime));
  59.       }
  60.     }
  61.   });
  62.   // 监听重置 loading 事件
  63.   uni.$off('resetLoading');
  64.   uni.$on('resetLoading', () => {
  65.     try {
  66.       resetLoading();
  67.     } catch (err){
  68.       console.log('---- resetLoading ----', err);
  69.     }
  70.   });
  71. }
  72. // 判断要不要隐藏loading
  73. const isCanHide = (key) => {
  74.   if (!loadingObj[key].show && Object.keys(loadingObj).length === 1) {
  75.     return true;
  76.   }
  77.   // 或者所有的loading对象的show都是false
  78.   for (const key in loadingObj) {
  79.     if (loadingObj[key].show) {
  80.       return false;
  81.     }
  82.   }
  83.   return true;
  84. }
  85. const resetLoading = () => {
  86.   loadingObj = {}
  87.   uni.hideLoading();
  88. }
  89. // 导出事件名称常量
  90. export const LOADING_EVENTS = {
  91.   SHOW: 'showLoading',
  92.   HIDE: 'hideLoading',
  93.   RESET: 'resetLoading'
  94. };
  95. // 导出默认对象,包含事件名称常量
  96. export default {
  97.   LOADING_EVENTS
  98. };
复制代码
使用方式:
在项目根目录main.js中初始化
  1. import { initLoading } from '@/utils/loading';
  2. initLoading()
复制代码
在需要的地方调用
  1. // 显示loading
  2. uni.$emit('showLoading', '提交订单中...', 'xxx');
  3. // 隐藏loading
  4. uni.$emit('hideLoading', 'xxx');
复制代码
使用注意事项

  • 其中xxx是key,要确保唯一,实在是怕重复的话可以引入第三方的uuid来做
  • 显示与隐藏要配套使用,不然记录的loading不会清空或者状态不会变,会导致问题
小结

这是我的 uni-app项目的loading使用方案,已使用在生产中,还没有发现什么问题,如果后续发现什么问题再做更新吧,解决方案千千万,世上没有最好,只有更好,如果你有更好的解决方案,可以分享出来,或者你发现此方案有什么问题,也可以提出来一起探讨。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册