8***@qq.com
8***@qq.com
  • 发布:2020-05-15 17:32
  • 更新:2020-07-17 14:27
  • 阅读:1495

【报Bug】基于swiper实现的自定义组件,小程序真机运行时,因为setTimeout延时异常导致明显卡顿,开发者工具正常

分类:uni-app

产品分类: uniapp/小程序/微信

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: win10 1903

HBuilderX类型: 正式

HBuilderX版本号: 2.6.16

第三方开发者工具版本号: 1.02.1911180

基础库版本号: 2.9.2

项目创建方式: HBuilderX

示例代码:

swiper是这么调用的

    <!-- 月历 -->  
    <swiper ref="swiper" :class="[ type === 'month' ? 'cm-calendar-swiper' : 'cm-calendar-swiper-week' ]" circular  
      :current="swiperIndex"  
      :duration="aniDuration"  
      @change="swipeHandler">  
      <swiper-item v-for="(count, index) in 3" :key="index">  
        <view class="cm-calendar-main">  
          <view class="cm-calendar-item" v-for="(day, dayIndex) in dateSet[index]"  
            :key="dayIndex"  
            :class="[ day.isHoliday ? 'holiday' : '',  
              date.rulue === day.rulue && day.calendarType === 'standard' ? 'selected' : '',  
              day.calendarType === 'prev' || day.calendarType === 'next' ? 'left' : '' ]"  
            @click="dayClickHandler(day)">  

            <view class="cm-calendar-item-up" style="height: 26px">  
              <view class="cm-calendar-day-text cm-text-16">{{ day.day }}</view>  
              <view class="cm-calendar-ganzhi cm-text-12">{{ day.ganzhi }}</view>             
            </view>  
            <view class="cm-text-12 cm-margin-top-3">{{ day.event }}</view>  

          </view>  
        </view>  
      </swiper-item>  
    </swiper>

以下是cm-calendar组件内部,滑动swiper回调处理日历数据的函数()
不要太在意业务逻辑,主要是几个console.time的位置,重点是“组件动画延时”这个定时器,这个定时器反应了setTimeout的延时时间问题很大。

    // 手势滑动触发事件  
    swipeHandler (e) {  
      if (this.swiperLock) {  
        // 当手操滑动对象时,不允许触发自动事件  
        return  
      }  
      console.log(e)  

      const index = e.detail.current  
      let sign = 'prev'  
      if (index === this.nextIndex) {  
        sign = 'next'  
      }  

      // 索引跟进  
      this.curIndex = index  
      // swiper控制器跟进  
      this.swiperIndex = index  

     /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!重点从这里开始!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */  
      console.time('组件动画延时')  
      console.log('组件动画开始', this.aniDuration)  
      clearTimeout(this.swiperHandlerTimer)  
      this.swiperHandlerTimer = setTimeout(() => {  
        // 动画完成后,再跟进日期和数据  
        if (this.swiperLock) {  
          // 双保险,当手操滑动对象时,不允许更新数据  
          return  
        }  
        console.timeEnd('组件动画延时')  
        console.time('日历数据更新')  
        // 日期跟进  
        if (this.type === 'month') {  
          // 月日期跟进  
          const tempDate = this.dateSet[this.curIndex][CM.Date.config.DAY_IN_WEEK]  
          let options = {  
            year: tempDate.year,  
            month: tempDate.month,  
            day: this.date.day,  
            hour: this.date.hour,  
            min: this.date.min  
          }  
          let newDate = new CM.Date(options)  
          while (!newDate.enabled && options.day > 0) {  
            // 遇上无效的,不存在的日期,要向前追溯  
            options.day--  
            newDate = new CM.Date(options)  
          }  
          this.date = newDate              
        }  
        else {  
          // 周日期跟进  
          const weekInDay = CM.Date.config.DAY_IN_WEEK  
          const startRulue = this.dateSet[this.curIndex][0].rulue  
          const desRulue = startRulue + this.date.weekDay  
          let options = {  
            rulue: desRulue,  
            hour: this.date.hour,  
            min: this.date.min  
          }         
          this.date = new CM.Date(options)             
        }  

        // 数据跟进  
        if (sign === 'next') {  
          const data = this.type === 'month' ?  
            CM.Date.getCalendarMonth(this.nextMonth.year, this.nextMonth.month) :  
            CM.Date.getCalendarWeek(this.date.year, this.date.month, this.date.day, 1)  
          this.$set(this.dateSet, this.nextIndex, data)  
        }  
        else {  
          const data = this.type === 'month' ?  
            CM.Date.getCalendarMonth(this.prevMonth.year, this.prevMonth.month) :  
            CM.Date.getCalendarWeek(this.date.year, this.date.month, this.date.day, -1)  
          this.$set(this.dateSet, this.prevIndex, data)  
        }  

        console.timeEnd('日历数据更新')  
        /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!重点j结束!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */  
        // 触发日期变动事件  
        this.onDateChange()      
      }, this.aniDuration)  
    },

操作步骤:

因为组件有一定的复杂度,不容易通过简单的代码复现,
我打包上传了工程文件,cm-calendar组件是问题组件
**复现成功了*****
我试着做了一个简单的复现,复现成功了,代码如下

<template>  
  <view>  
    <swiper style="height: 200px; border: 1px solid black; margin-top: 60px;" circular @change="changeHandler">  
      <swiper-item>第一页</swiper-item>  
      <swiper-item>第二页</swiper-item>  
      <swiper-item>第三页</swiper-item>  
    </swiper>  
  </view>  
</template>  

<script>  

export default {  
  data () {  
    return {  
      duration: 300  
    }  
  },  
  methods: {  
    changeHandler(e) {  
      console.log(e)  

      console.log('开始延时', this.duration)  
      console.time('延时')  
      clearTimeout(this.timer)  
      this.timer = setTimeout(() => {  
        console.timeEnd('延时')  
      }, this.duration)  
    }  
  }  
}  

</script>  

<style>  
</style>

预期结果:

真机能够与开发者工具中的运行效果一致

实际结果:

复现结果:开发者工具,期望结果
开发者工具控制台

复现结果:真机,异常结果
真机

bug描述:

我基于循环swiper实现了一个滑动日历组件,滑动时监听swiper的change事件,在回调函数中更新日历数据,为了不影响swiper的动画,我给更新数据操作设置了200ms的延时。

此组件在微信开发者工具中运行顺畅,然而发布为体验版,或者真机调试时,明显发现组件更新日历数据的延迟不正常。

开始我以为是自己算法效率的问题,但是查了一圈发现是真机环境下setTimeout延迟了更多的时间(远远不止200ms)。

我在控制台打印了相应的耗时信息如下。

开发者工具控制台

真机测试控制台

相关代码会放在代码示例

2020-05-15 17:32 负责人:无 分享
已邀请:
DCloud_UNI_GSQ

DCloud_UNI_GSQ

你还是得回到开始的排查点去:“开始我以为是自己算法效率的问题”
因为是单线程的,setTimeout 超出设定的时间,多半是由于其他的地方计算时间长导致的。

DCloud_UNI_GSQ

DCloud_UNI_GSQ

电脑上没问题,应该是因为电脑的cpu比手机的计算能力强。

  • 8***@qq.com (作者)

    你可以看一下我的复现代码,setTimeout内外没有任何耗时操作,即使如此,依然出现了极大的延迟。

    这足以说明,在小程序的真机运行环境,每一次触发swiper的滑动时,swiper组件本身一定做了什么操作阻塞了线程。

    2020-05-18 16:56

  • 8***@qq.com (作者)

    另外,这个问题只存在于小程序端真机,开发者工具,H5,APP是正常运行的。

    2020-05-18 16:57

  • DCloud_UNI_GSQ

    回复 8***@qq.com: 也是可能的,你切换一下基础库版本试试,看是不是小程序新加的bug。

    2020-05-18 17:02

  • 8***@qq.com (作者)

    回复 DCloud_UNI_GSQ: 这就很难了,开发者工具降级基础库版本并不能同步到真机的样子。

    2020-05-18 17:21

  • DCloud_UNI_GSQ

    回复 8***@qq.com: 那就再换几个角度排查:1. 搜索微信社区是否有相关反馈 2. 在自己同步计算的一些地方打印执行时间,如果执行时间长就是自己问题。

    2020-05-18 17:27

  • 8***@qq.com (作者)

    回复 DCloud_UNI_GSQ: 好吧,我的问题,对小程序了解的太少了,我看了下小程序文档,用原生wxml写了一个同样的复现demo,出现了一样的问题。

    看样子uni-app在小程序端应该只是转译了一下代码语法,并没有过多地做其他事情。

    2020-05-18 17:51

  • DCloud_UNI_GSQ

    回复 8***@qq.com: 赞一个,期待你继续排查找到问题根源

    2020-05-18 17:58

booooom

booooom - booooooom

老哥,找到解决的问题了吗?

  • 8***@qq.com (作者)

    微信小程序原生的问题,没办法,只能避免在swiper的回调函数里用setTimeout,想别的实现方式绕开吧

    2020-07-27 15:01

该问题目前已经被锁定, 无法添加新回复