记录在Taro3(H5)中开发的一系列兼容性问题

1,672 阅读5分钟

已经好久都没有写过文章了 因为最近比较懒惰 哈哈哈 公司在最近使用taroui3开发项目 遇到的一些坑 记录一下

一、输入框回车功能

业务场景

image.png

如上图 需要在输入完内容 如快递单号等 进行搜索

解决方法

因为没有重置按钮等 所以就需要利用弹框的回车功能 因为使用的是taro自带的UI框架taro-ui,文档翻阅了一圈都没有发现有回车事件 故打算监听页面的按键事件并根据相对应标识来判断 经过测试onkeydown中并没有相关标识 onkeyup是有的 故使用onkeyup事件:

componentDidMount() {
    ...
    //h5:没有回车事件 故监听keyup keydown也无效
    window.addEventListener('keyup', this.handleSubmit)
}
componentWillUnmount() {
    window.removeEventListener('keyup', this.handleSubmit)
}
handleSubmit = (e) => {
    console.log('e', e)
    if (e.code === 'Enter' && this.props.onInputConfirm) {
      this.props.onInputConfirm({ isResetList: true })
    }
 }

安卓遇到问题

在开发者工具上和苹果真机上没有问题 然后在安卓手机上发现了问题 经过测试 安卓手机的code为空 故增加判断

handleSubmit = (e) => {
    console.log('e', e)
    //苹果回车键which=13 完成键=55/88 
    if ((e.code === 'Enter' || e.key === 'Enter' || e.which === 13 || e.which===55 ||e.which===88) && this.props.onInputConfirm) {
      this.props.onInputConfirm({ isResetList: true })
    }
  }

2、筛选日期功能

业务场景

image.png

如上图需要进行日期筛选

贴一个计算近N天时间戳的方法

//计算不同时间区间的起始时间戳
export function calculateTimestamp(type) {
  let timeArr = []
  switch (type) {
    case 'today':
      timeArr = [getTimetampFrom(`${getDay(0)} 00:00:00`), getTimetampFrom()]
      break
    case 'yesterday':
      timeArr = [
        getTimetampFrom(`${getDay(-1)} 00:00:00`),
        getTimetampFrom(`${getDay(0)} 00:00:00`)
      ]
      break
    case 'recently7':
      timeArr = [getTimetampFrom(`${getDay(-7)} 00:00:00`), getTimetampFrom()]
      break
    case 'recently30':
      timeArr = [getTimetampFrom(`${getDay(-30)} 00:00:00`), getTimetampFrom()]
      break
    default:
      timeArr = []
      break
  }
  //配合后端返回对应值
  return timeArr.map((time) => time / 1000)
}

function getDay(day) {
  var today = new Date()

  var targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day

  today.setTime(targetday_milliseconds) //注意,这行是关键代码

  var tYear = today.getFullYear()

  var tMonth = today.getMonth()

  var tDate = today.getDate()

  tMonth = doHandleMonth(tMonth + 1)

  tDate = doHandleMonth(tDate)

  return tYear + '-' + tMonth + '-' + tDate
}

function doHandleMonth(month) {
  var m = month
  if (month.toString().length == 1) {
    m = '0' + month
  }

  return m
}

//得出时间戳
function getTimetampFrom(day) { 

  if (!day) {
    return new Date().getTime()
  } 
 
  return new Date(day).getTime()
}

IOS中遇到问题

突然一个测试小哥跟我说 hey boy 你的这个筛选时间不生效 我心想怎么可能 原来真的不生效 但是在我的安卓手机上是生效的 心里一万头草泥马飘过 经过我的一番调试 原来是new Date这一步出现了问题 在IOS中 new Date的入参不能是xxxx-xx-xx 这样会被解析成NaN 必须要使用xxxx/xx/xx的形式 故进行一下代码的修改

//增加判断ios
function isIos() {
  let u = navigator.userAgent
  let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
  let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) //ios终端
  return isiOS
}

import { isIos } from './index'

//得出时间戳
function getTimetampFrom(day) { 

  if (!day) {
    return new Date().getTime()
  }

  if (isIos()) {
    //IOS不兼容new Date()
    day = day.replace(/\-/g, '/')
  } 

  return new Date(day).getTime()
}

3、picker组件IOS下存在滚动穿透

业务场景

image.png

如上图需要选择快递

IOS遇到问题

测试小哥说 我发现你这个有一个小bug啊 你滚着滚着 就上去了 我一看好像是这样 然后我发现了凡事使用AtFloatLayout组件都有这个问题

1.touchmove+event.preventDefault()

不过我也是这么做的 并没有生效

export function stopPropagation(event) {
  event.stopPropagation()
}

export function preventDefault(event, isStopPropagation) {
  console.log('preventDefault', event)

  /* istanbul ignore else */
  if (typeof event.cancelable !== 'boolean' || event.cancelable) {
    console.log('preventDefault2', event)
    event.preventDefault()
  }

  if (isStopPropagation) {
    stopPropagation(event)
  }
} 


const handleTouchMove = (event) => {
    move(event)

    if (isVertical()) {
      console.log('isVertical()')

      moving.current = true
      preventDefault(event, true)
    }

    let rangeOffset = range(startOffset.current + deltaY, -(count() * itemHeight), itemHeight)

    setOffset(rangeOffset)

    const now = Date.now()

    if (now - touchStartTime.current > MOMENTUM_LIMIT_TIME) {
      touchStartTime.current = now
      momentumOffset.current = rangeOffset
    }
}

2.将body的overflow设置为hidden

事实证明 ,这个完全管用

useEffect(() => {
    if (visible) {
      window.document.body.style.overflow = 'hidden'
    } else {
      window.document.body.style.overflow = 'auto'
    }
    () => {
      window.document.body.style.overflow = 'auto'
    }
}, [visible])

4、底部absolute定位被顶上来

业务场景

1625825374960.jpg

如上图需要绑定手机号

安卓遇到问题

由于测试小哥是IOS系统所以不存在这个问题,产品对我说 宝 崩了啊 我心头一颤 说这个logo被顶上来了啊 我心想:产品 我劝你不要多管闲事

开始设想

首先页面为父组件 高度为视图高度 定位为absolute logo为absolute bottom为40px 是不是由于安卓在键盘弹起后将高度认为是中间未被键盘覆盖的那部分呢 给上面设置最小高度 flex布局均没有效果

解决方法

比较粗暴 当focus时 也就是键盘弹起时将logo隐藏 然后blur时 将logo显示


handleFocus = () => {
    console.log('handleFocus')
    this.setState({
      showLogo: false
    })
}

handleBlur = () => {
    console.log('handleBlur')
    this.setState({
      showLogo: true
    })
}

{showLogo && (
          <View className='auth-ft'>
            <Image className='logo' mode='widthFix' src={LOGO} />
          </View>
 )}

希望更优解决方法

虽然这种方法也算是解决了问题 但是不是很好 对我来说 我过不了我心里那关 但是产品说OK 我觉得就OK hahaha 但是还是希望有人可以提出更好的方案

5、手机拍照功能

业务场景

image.png

如上图需要拍照并上传头像

IOS遇到问题

测试小哥说IOS为啥拍照上传没有反应呢?然后我用IOS测试了一下 果然如此

不断调试

经过我的不断调试(console.log)发现了 可能是qiniu-js这个库的原因 并报出了这么一个错

WechatIMG103.jpeg

英文灰常不错的我一眼就看到了错误原因“FileSizeLimit” 这不就是文件大小过大吗 ? 我心想 难道是ios拍照时的图片质量太好 过大了?不过我在电脑端试了100M的图片 都可以完美上传,我打开了qiniu-js 的github 给他配置了个参数retryCount=10,表示上传失败重试次数,但是上传了貌似还是没有效果 所以我就接着官网的一个上传前压缩的代码试了下 看看有没有效果 哎果不其然 苹果也可以了 不知道是不是与苹果h5的上传限制有关

 qiNiuInit(tokenRes) {
    this.client.upload = (flie, key) => { 
      const config = {
        retryCount: 10
      } 
      const options = {
        quality: 0.1,
        noCompressIfLarger: true
      }
      return new Promise((resolve, reject) => {
        QiNiu.compressImage(flie, options).then((data) => {
          console.log('data', data)
          const observable = QiNiu.upload(data.dist, key, tokenRes.token, {}, config)
          // const subscription = observable.subscribe(observer) // 上传开始
          observable.subscribe({
            next: (res) => {
              console.log('next', res, res?.uploadInfo, res?.chunks, res?.total)
            },
            error: (err) => reject(err),
            complete: (res) => resolve(res)
          })
        })
      }) 
    }
  }