Hybrid App 踩坑(移动端H5常见bug)

2,063 阅读9分钟

前言

记录Hybrid App 内嵌 H5开发中常遇到的一些bug

Hybrid App,Hybrid(混合),就是原生App里打开webApp,相当于native提供了浏览器内核打开web页面,当web页面交互比较多会有很多兼容问题。

感觉很多bug在一般手机浏览器里都是不存在的,但在App里打开就有了,最开始是在微信浏览器里出现bug。后面从笔者开发 App内嵌H5页时就遇到了一些bug,简单记下

1. iphone上的input fixed定位

  • 在iphone上,按钮点击弹出一个文本框,用户可以在文本框输入,为了有更好的体验,在点击按钮的时候会给文本框添加一个获取焦点事件,这样点击完按钮就直接输入而不必再点一下文本框输入,若页面上有定位就容易失效,如果是 position:fixedfocusfixed 会失效,以致页面排版出现问题,用户体验不好。

    解决方法: 尽量别用定位,非得用就用absolute

2. rem布局的一些问题

  • 1.rem布局,可能会在三星note4、红米等手机上出现页面排版混乱的问题,rem还是慎用,移动端布局可以用flex

    解决方法:非得用rem的话就只能一点点调整根元素大小,也可用 navigator.userAgent 获取手机型号,单独给不兼容的手机提供一个 font-size
  • 2.项目里的border用rem单位,在有些安卓手机上是会显示不出来。

    解决方法:直接用 px 
    

3.line-height属性在在安卓手机表现有问题

  • 一行文本想垂直居中,最简单的办法就是把 line-height 设置为文本所在标签的高度,但是在安卓手机上会有问题。

    解决方法: 安卓手机单独设置 line-height。比如本来标准的 line-height 是0.5rem,但是在安卓上设了0.35rem, 或者给一点 padding-top,网上有很多解决办法,但是都不怎么靠谱。也不知道具体兼容性,可以特殊处理

4. iphone 文本框输入完点回车操作会有一个问题

  • 1、键盘上的搜索键

    可能显示的不是‘搜索’这两个字,显示的就是‘下一项‘,有的显示的是‘换行’,这样体验也不是很好,有些人肯定输入完以后没看到搜索按钮会懵逼,所以把键盘的enter键的字改成搜索是有必要的

    解决方法:input框用form标签包起来即可

  • 2、移动端做搜索的时候用到keyup或者keydown事件在iOS上的兼容问题

    iOS自带的键盘有一部分是不触发键盘事件的,如输入了shi,键盘上方会有一排待选的字,点击选择字,这部分是不触发键盘事件的,所以说如果我选了个“是”字,是不会触发搜索的,这算个bug吧,第三方输入法上不会存在,但iOS用自带键盘的用户也很多,所以还是得解决

    解决办法: 就是将键盘事件换input propertychange事件就可以; 例:把$('.search').keyup(function(){})换成$('.search').on('input propertychange',function(){}), 是js原生的方法,就是oninput,类似于onclick,可以实时监听文本框的内容。 (该bug在手机浏览器和App里均会出现)。

5. 移动端如果要用css3动画

必须要加前缀,-webkit-,否则在安卓手机上动画会失效,iOS是没问题。

6. 移动端 click 事件300毫秒的延迟 与 click 穿透

  • 1.为什么会产生延迟

    iOS 中的 safari,为了实现双击缩放操作,在单击 300ms 之后,如果未进行第二次点击,则执行 click 单击操作。也就是说来判断用户行为是否为双击产生的。但是,在 App 中,无论是否需要双击缩放这种行为,click 单击都会产生 300ms 延迟。

    最好用touch事件,别用click,非用click,且是在手机浏览器里打开的话click事件也能正常执行,看不出来延迟效果,至少我没看出来,可以直接用click。 但是如果要在App里打开的话,click事件的延迟十分明显

      解决方法:
      1、把click事件全部改为touch事件
      2、利用fastclick插件,可以清除click事件的300毫秒延迟,但fastclick好像不小
    
  • 2.为什么会click穿透

    双层元素叠加时,在上层元素上绑定 touch 事件,下层元素绑定 click 事件。由于 click 发生在 touch 之后,点击上层元素,元素消失,下层元素会触发 click 事件,由此产生了点击穿透的效果。

      解决方法:1、与上一个问题类似,使用 touchstart 替换 click,
      将 click 替换成 touchstart 不仅解决了 click 事件都延时问题,还解决了穿透问题。
      因为穿透问题是在 touch 和 click 混用时产生。
    

    一般的触摸滑动事件的触发过程 是 touchstart - touchmove - touchend - click

7. ios滑动卡顿问题

在ios浏览器中上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。

解决方法:
1.在滚动容器上增加滚动 touch 方法
将-webkit-overflow-scrolling 值设置为 touch
body { -webkit-overflow-scrolling: touch;}
设置滚动条隐藏:body ::-webkit-scrollbar {display: none;}

2.设置 overflow
设置外部 overflow 为 hidden,设置内容元素 overflow 为 auto。内部元素超出 body 即产生滚动,超出部分 body 隐藏。
bodyoverflow-y: hidden;} .wrapperoverflow-y: auto;}复制代码

8. 页面放大或缩小不确定性行为

双击或者双指张开手指页面元素,页面会放大或缩小。
HTML 本身会产生放大或缩小的行为,比如在 PC 浏览器上,可以自由控制页面的放大缩小。但是在移动端,我们是不需要这个行为的。所以,我们需要禁止该不确定性行为,来提升用户体验。
HTML meta 元标签标准中有个 中 viewport 属性,用来控制页面的缩放,一般用于移动端。

解决方法:
head标签里 设置 meta
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">

9. iPhone X系列刘海屏安全区域适配问题

头部刘海两侧区域或者底部功能条区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域

解决方法:
设置安全区域,填充危险区域,危险区域不做操作和内容展示。
head 标签中 meta标签 viewport-fit 设置为 cover,获取所有区域填充。 判断设备是否属于 iPhone X,给头部底部增加适配层
// 设置 viewport-fit 为 cover
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover">

// 使用 safe area inset 变量

/* 适配 iPhone X 顶部填充*/
@supports (top: constant(safe-area-inset-top)) or 
(bottom: constant(safe-area-inset-bottom)){ 
    body, .header { 
        padding-top: constant(safe-area-inset-top, 40px); 
    }
    body, .footer{ 
        padding-bottom: constant(safe-area-inset-bottom, 20px); 
    }
}
/* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
@supports (bottom: env(safe-area-inset-top)) or
	(bottom: env(safe-area-inset-bottom)) {
    body, .header { 
        padding-top: env(safe-area-inset-top, 40px); 
        padding-top: var(safe-area-inset-top, 40px); // env 不生效时触发
    }
    body, .footer{ 
        padding-bottom: env(safe-area-inset-bottom, 20px); 
    }
}

10. 微信公众号分享问题

在微信公众号 H5 开发中,页面内部点击分享按钮调用 SDK,方法不生效。

解决方法:添加一层蒙层,做分享引导即可。

11. H5 调试相关方案策略

  • 使用 vsconsole
// 导入
npm install --save-dev vsconsole

// 使用
import Vconsole from 'vconsole'
new Vconsole()
  • chrome://inspect/#devices chrome 浏览器直接使用,手机端要开启USB调试功能,访问页面就能直接看到,可以直接使用chrome的扩张调试一些手机的功能

12. Android 与 H5交互数据的一些注意点

  1. 对 Android 传的 \ 的处理 Android 和前端交互只能传字符串,iOS 可以传对象类型,所以遇到对象类型只能让安卓转成JSON字符串前端再解析,但是遇到反斜杠\就会出错,JSON.parse 解析时直接报错

这是 Android 传过来的字符串就有问题,当出现\ 时,由于 JavaScript 中它是个转义符,所以JSON.parse的时候\\会被解析成\,一个反斜杠就直接报错

安卓返回的反斜杠都缺了一半那就手动给它补上一半,直接匹配安卓传给前端的字符串用String.prototype.replaceAll()去转换,JSON.parse 解析后,在替换回来就行

var data = '{"a":"\\"}'
var data2 = data.replaceAll('\\','\\\\')
JSON.parse(data2) // 正常解析出 {a: '\\'}
  1. H5 传给 Android 数据有 + 号的注意点 需要先在前端将 + 转成 %2BAndroid 解析才不会出错
    这通常出现在需要给 Android 传递base64时需要用到,因为base64编码有 + 号,同样使用String.prototype.replaceAll()去做转换
var data = {"a":"base64+324qwe+ABKGF"}
data.a = data.a.replaceAll('+', '%2B')
data3 = JSON.stringify(data) // '{"a":"base64%2B324qwe%2BABKGF"}' 传递给 Android 

13. Native 中 H5 video 播放问题

Android 非全屏和全屏模式使用的都是 h5的播放器。h5的播放器在安卓的webview内是有一些问题的。

  1. 进入全屏后,看一段时间后自动锁屏(应该是h5 的播放器没有触发系统的常亮功能)

    解决:客户端定义一个方法,H5端在点击全屏的时候,通知客户端,不要熄屏;
    退出全屏的时候,再告诉客户端,可以恢复正常的熄屏功能。

  2. 进入全屏后,再退出全屏,页面自动回到顶部,这个也是贼拉奇怪

    解决:在点击全屏时,mark 一下当前屏幕的滚动高度。
    退出全屏时,再设置当前页面滚动到指定位置,用户并感知不到主动设置页面滚动到指定位置,异常丝滑

function enableFullScreen (videoeData) {
  // 点击全屏按钮时,记录当前页面的滚动位置
  this.scrollTop = document.documentElement.scrollTop || document.body.scrollTop

  // 安卓进入全屏播放,需要告诉客户端不熄屏
  if (dsbridge.hasNativeMethod('common.isKeepScreenOn')) {
    dsbridge.call('common.isKeepScreenOn', 'true')
  }
},

cancelFullScreen () {
  // 安卓退出全屏后,关闭屏幕常亮模式
  if (dsbridge.hasNativeMethod('common.isKeepScreenOn')) {
    dsbridge.call('common.isKeepScreenOn', 'false')
  }

  // 由于安卓退出全屏后,默认情况下页面会自动回到顶部,这里需要处理一下
  if (utils.isInAndroidApp()) {
    document.documentElement.scrollTop = this.scrollTop
    document.body.scrollTop = this.scrollTop
  }
}

最后

以上的方式总结只是自己学习总结,有其他方式欢迎各位大佬评论
渣渣一个,欢迎各路大神多多指正,不求赞,只求监督指正( ̄. ̄)
有关该文章经常被面试问到的可以帮忙留下言,小弟也能补充完善完善一起交流学习,感谢各位大佬(~ ̄▽ ̄)~