cloudfroster / mobile-develop-suggest

移动开发建议汇总

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mobile-develop-suggest

移动开发建议汇总

what

全面收集开发疑难问题的文档,详细解释问题出现的原因和相应的解决方案,大多数问题一般只会给出解决方案,而这个里,你将找到问题的根源。

why

在我们开发的过程中,难免会遇到一些问题或者坑,我们通过 google, baidu 等渠道去查询问题,有的回答已经过时,有的没有给出解答或者和你遇到的不一致,终于你在某篇文章中找到答案并且在你的项目中验证通过。修复相应的问题后,你就把问题抛之脑后或者随手记录个博客,然后呢,一位新来的或者你的同事由于做的事情和你相识,所以也极可能会遇到和你相关的问题,由于没有文档,而在没有咨询你的情况下,他也只能再次和你走一样的流程,网上去各种查找资料,这样极大的浪费时间。所以才有了这个项目,主要目的是在团队内部或者公司层面建立疑难问题知识库,促进分享和总结,把你遇到的疑难问题和相应的解决方案分享出来,这样可以极大的减少其他人查找相同问题的时间。

这个仓库是想建立一个疑难问题的知识库,如果问题在网上能轻易找到解决方案的话,并不需要在这里罗列。 建议收集比较难以查到的问题或者极易忽悠的问题。目前项目主要收集的是移动端 h5 页面开发的相关疑难问题,后续可以增加其他开发技术。

how

贡献此仓库也请遵循 3 个 W 即可: what why how

  • what: 介绍是什么问题和问题出现的条件
  • why: 详细介绍问题出现的原因
  • how: 如何修复这类问题或者如何避免

H5

移动端点击事件延迟 200-300ms 触发

What

在移动端会存在 300ms 点击(click)事件的延迟,用户点击后会感觉卡顿。

Why

以下是历史原因:

2007年苹果发布首款iphone上IOS系统搭载的safari为了将适用于PC端上大屏幕的网页能比较好的展示在手机端上,使用了双击缩放(double tap to zoom)的方案,比如你在手机上用浏览器打开一个PC上的网页,你可能在看到页面内容虽然可以撑满整个屏幕,但是字体、图片都很小看不清,此时可以快速双击屏幕上的某一部分,你就能看清该部分放大后的内容,再次双击后能回到原始状态。

双击缩放是指用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。

原因就出在浏览器需要如何判断快速点击上,当用户在屏幕上单击某一个元素时候,例如跳转链接,此处浏览器会先捕获该次单击,但浏览器不能决定用户是单纯要点击链接还是要双击该部分区域进行缩放操作,所以,捕获第一次单击后,浏览器会先Hold一段时间t,如果在t时间区间里用户未进行下一次点击,则浏览器会做单击跳转链接的处理,如果t时间里用户进行了第二次单击操作,则浏览器会禁止跳转,转而进行对该部分区域页面的缩放操作。那么这个时间区间t有多少呢?在IOS safari下,大概为 300ms。这就是延迟的由来。造成的后果用户纯粹单击页面,页面需要过一段时间才响应,给用户慢体验感觉,对于web开发者来说是,页面js捕获click事件的回调函数处理,需要300ms后才生效,也就间接导致影响其他业务逻辑的处理。

导致这个问题的根本原因在于 IOS 早期为了方便用户看清手机端的文字。引入了双击缩放页面的操作。而双击缩放正是导致 click 事件延迟的原因。事件触发顺序如下:

touchstart -> touchend -> wait 300ms (when touch again then double tap) -> click

在 touchend 事件结束后,浏览器会等待 300ms,来确定用户是否需要双击来缩放页面。如果在 300ms 内没有操作,才触发 click 事件。

而随着移动页面的兴起,大量页面是对手机友好的,不需要缩放的。所以没必要等 300ms 来缩放页面。

How

1. html meta 标签方案:

chrome 32 开始支持 meta 标签

<meta name="viewport" content="width=device-width">

如果页面中有该 meta 标签就会取消双击缩放。从而解决300ms 延迟。

  1. css touch-action 方案:

touch-action 是一个新的 css 属性,用于指定某个给定的区域是否允许用户操作,以及如何响应用户操作(比如浏览器自带的划动、缩放等) 所以可以用如下 css 来阻止 300ms 延迟:

hmlt {
    touch-action: manipulation
}

不过目前这个属性存在兼容问题。IOS 10 只支持部分属性(none, manipulation)。网上也有许多 polyfill 库来支持该 css 属性。比如:hammer-time.js

  1. js fast-click 方案:

fast-click 会在 touchend 事件触发后,自定义事件来触发 click 事件。并且会阻止 300ms 后原生的 click 事件。从而达到消除 300ms 延迟的效果。 由于该库存在许多问题未修复,作者多年不维护,而且是一个 2 年前的库了。所以能不用尽量不用。fast-click 内部也会根据浏览器来智能判断是否启动模拟事件代替原生 click 事件。以下为详细判断条件:

1. 所有桌面浏览器不会生效。
2. chrome 版本高于 32 的不会生效。
3. 如果传入进去的元素支持 css 的 touch-action 属性 none 或者 manipulation 则不会生效。所以调用前可以设置下元素的 touch-action 属性。

如果引入 fast-click,有一些问题是该库的 bug,其中一个是如果采用全局滚动,在 ios 上惯性滚动期间,点击 fixed 定位在头部的元素,会发生点击穿透的情况。

归纳一下:

由于 android 2.2 以后都支持 meta 标签来消除 300ms 延迟,所以所有的安卓机器基本可以认为原生消除了 300ms 延迟。

IOS 10 以后支持 touch-action 属性(是这样说的)。

对比目前公司浏览器的数据:
    IOS 最低版本:iOS - 9.3.2 占比 1.05%。
    android 最低版本:Android - 4.4.2 占比 1.14%。
测试下来目前饿了么采用的是 uiwebview, IOS meta 和 touch-action 失效,还是存在点击延迟,所以目前 ios上需要引入 fast-click。

扩展阅读列表: the-touch-action-css-property fastclick


滚动行为

What

目前有 3 中滚动行为:

  1. 全局滚动是在 html 或者 body 上滚动。优点是简单,滚动流畅。
  2. 局部滚动是在一个独立的元素上,设置相应的高度,当内容高度超2过容器高度后,设置overflow:auto样式,即可产生局部滚动效果。
  3. 模拟滚动是通过 js 来监听 touch 事件从而控制相应的元素位置。采用的比较多的是 translate,不支持 css3 可以降级为 top,left 之类的定位。

Why

全局滚动在最新的 ios11 uiwebview 中采用 fixed 或者 sticky 定位后的元素在滚动的过程中会消失。由于一些页面头部是 fixed 定位,滚动后导致头部消失,让页面看起来十分怪异。所以如果页面头部是 fixed 定位的话,目前阶段并不建议采用全局滚动方案。这个目前没有比较好的方案来解决,只能等等看苹果是否能给出相应的解决方案或者修复掉这个问题。所以现阶段只能采用局部滚动或者模拟滚动。(如果页面中 fiexed 或者 sticky 定位的元素比较重要的话)

局部滚动在低端的安卓机器上,会有卡顿的效果,影响用户体验,并不是十分推荐。或者可以做成低端安卓全局滚动。IOS 机器采用局部滚动。不过这样一来,维护成本增加。局部滚动还会有局部滚动和全局滚动切换的问题,可以采用scrollFix.js来解决。

模拟滚动的优点是各种下拉刷新,上拉刷新都有比较好的支持。同时在各个平台滑动体验比较一致,在安卓端也可以实现类似 IOS 的回弹效果。不过弊端也很明显,由于滚动是监听滑动事件来动态计算元素的布局,并用 transfrom 改变元素位置来实现的,所以性能不是很理想,特别是在 uiwebview 中,同时由于是 transfrom,原生的事件比如 click 不能触发,必须用框架自带的事件。(新零售尝试结果是页面滚动不平滑,跟手效果不明显,有卡顿,因为图片太多,不建议采用该种方式滚动)。

How

首先最推荐的是全局滚动,性能好,而且布局简单。但是如果你的页面有较重的定位元素,在 ios11 中会消失的话,应该考虑用局部滚动来实现。而模拟滚动在目前的环境下不是很推荐。体验不是很好。

目前新零售采用的是统一局部滚动的方案。比较好的理想解决方案是:IOS局部滚动(解决滑动消失问题)安卓局部滚动。等IOS把滚动消失问题解决后。统一为全局滚动。

相关实验结果: 滚动实验:分别采用 better-scrolliscroll 体验上 iscroll 对于平滑的处理更加好,滚动比较流畅,但是慢慢滑动还是有卡顿的效果,这个产品上个人觉得很差劲,用原生滚动是目前比较合适的方案。


ios 局部滚动卡页面

What

在 IOS 机型中,局部滚动如果滚动到顶部或者底部,在往上或者往下滑动,就会变成全局滚动,这个时候如果反方向滑动的话,并不会触发元素的局部滚动,会卡住页面。

Why

由于页面采用的是局部滚动,所以整个页面高度其实就是手机的高度。而在局部滚动达到滚动边界的时候,滚动的行为会往上层传递到达 html 层。这个时候就变成了全局滚动了(有点类似于 dom 的事件传递机制)。这个时候你在手指不离开页面的话,并不会让滚动变为局部的,导致整个页面卡住。

How

幸运的是,这个看起来是浏览器默认的行为,还是有 hack 方式可以解决的,其中如果用 js 的方案可以采 scrollfix.js 库来解决。该库的原理就是监听 touchstart 事件判断如果是局部滚动边界情况下,会自动往下或者往上滑动一像素,从而避免局部滚动变为全局滚动。

当然我们还可以采用 css 来避免,目前有一个 overscroll-behavior 的 css 属性提案,该属性比较新,目前支持浏览器还比较少。用来控制当滚动到边界的时候,元素应该如何滚动。

overscroll-behavior 属性有三个值:

  • auto:默认值。元素(容器)的滚动会传播给其祖先元素。有点类似 JavaScript 中的冒泡行为
  • contain:阻止滚动链接。滚动行为不会传播给其祖先元素,但会影响节点内的局部显示。例如,Android 上的光辉效果或iOS 上的回弹效果。当用户触摸滚动边界时会通知用户。(overscroll-behavior:contain 在 html 元素上使用,可以阻止导航滚动操作)
  • none:和 contain 一样,但它也可以防止节点本身的滚动效果

这个还是只能等浏览器兼容性比较好的时候再采用,不过也可以提前写入代码中,并不影响功能。


滑动穿透

What

滑动穿透问题是指浏览器在对局部元素内部滑动到边界后,继续滚动会滚动全局的页面。

Why

这个问题的本质是浏览器滚动行为是有传递功能的,会传递到上层,触发页面的滚动。

How

目前可以采用 overflow 方案:

1. 安卓可以采用 fixed 定位。ios如果用 fixed 定位会闪屏

2. 安卓局部滚动可以将父元素用 `height: 100%; overflow: hidden` 来阻止滚动

IOS 跳转原来位置闪屏

What

在 IOS 中,如果记录一个元素的滚动位置,之后快速的回退到改位置的话,会有白色的闪屏效果。

Why

由于我们跳转到一个新的页面,从而丢失了原来页面滚动位置的信息,从而让回到该新页面后还是头部,于是我们手动去触发页面的 scroll。滑动到原先的位置,在安卓中,并不会出现什么问题,可以在 ios 中,会出现白色的闪屏现象,因为 ios 处理回退渲染的时候,会让用户看到白色的界面,然后在跳转到页面的位置。

How

可以采用局部滚动的方式。如果有其他更加好的方案,欢迎补充。


scroll 事件在 IOS 上非实时触发

What

在 PC 中 scroll 事件指的是页面滚动,这个是一个实时的事件,触发频率非常的高,在移动端安卓也是实时触发该事件的,可是在 IOS 上, scroll 事件却不是实时触发的,它需要等到页面的惯性滚动结束后,才会触发事件,这让一些基于页面滚动的特效不是很理想。

Why

简单说这个和 IOS 底层架构有关,安卓采用的架构会先处理 UI 和 JS 事件。IOS为了让滚动更加流畅。会阻塞相关的JS事件和动画效果比如过渡。也就是说IOS在惯性滚动期间,会阻塞js和渲染(过渡,动画)。同时触发 scroll 事件只会在那个回弹效果完成后触发一次。导致想要在滚动过程中判断相应高度从而做页面的改变是不行的。

具体来说是(引用自互联网):iOS 最先响应屏幕反应。响应顺序依次为Touch——Media——Service——Core架构,当用户只要触摸接触了屏幕之后,系统就会最优先去处理屏幕显示也就是Touch这个层级,然后才是媒体(Media),服务(Service)以及Core架构。当系统接收到Touch事件之后会优先响应,此时会暂停屏幕上包括js、css的渲染。这个时候不光是css动画不动了,就连加载也会停止。

How

如果要实现滑动实时的效果,我们在 ios 上不仅仅需要监听 scroll 事件,同时还要监听 touchmove 事件,可以把相关的逻辑写入到公共函数中,然后 touch 事件可以处理手指在屏幕上移动时候的逻辑,scroll 事件可以处理滑动停下来的逻辑。不过在惯性滚动期间,页面并不会有计算逻辑,这样会让页面的动销不平滑。一个简单的方式是用轮询的方式来填充。


安卓中小字体上移

What

我们常采用的单行文字居中是用如下的 css:

/* line-height 和 container 的高度一致 */
.text {
    height: 20px;
    line-height: 20px;
}

/* flex 定位实现居中 */
.text {
    display: flex;
    align-items: center;
}

/* padding 撑起来假装居中 */
.text {
    padding: 5px 0;
}

这样在 PC 端是没问题的,在移动端也是没问题的,不过当文字的字体小于 12px 的时候,如果加上背景,就会发现文字往上移动,并不是垂直居中的。(只在安卓的 uiwebview 有这样的情况)

Why

这个和文字大小有关系,不过具体原因未知。

How

解决方案有多种:

  1. 可以用 line-height 来动态调整下文字的位置,让文字居中,不过具体调多少,并没有明确的说明。不过这是个很简单的方式。
  2. 可以用缩放的方式来实现。也就是第一步,将你的文字放大2倍。然后用 transfrom 的 scale 缩小 2 倍即可。同时需要注意的是,保留一个原来的元素来占位置(让站位元素不可见,但是任然占用页面空间,同时采用 relative 定位,然后子元素是站位元素的放大版,同时缩放即可)。

Vue

About

移动开发建议汇总