imweb/mobile

Mobile开发经验沉淀

herbertliu opened this issue · 28 comments

在手Q开发过程中,遇到各种问题,解决之后沉淀如下,细分为:

1、经验沉淀:开发过程怎么快速开发并在开发过程中可以兼顾到一些场景的出现
2、疑难问题:一些机型出现的疑难问题,如和解决,以及对应的机型等问题详细描述(截图)

组织形式可以用问答的方式吧。 一个帖子逻辑扩展性比较差。

@webryan 先在这里沉淀,多个帖子不太好找,可以在这里讨论,知识的整理在基于这里的沉淀。看讨论过程也是一中享受。而且这里有个好处可以看到历史的脚印。

@litten 这块知识体系没建立之前,先这么搞吧。 合理的方式应该是知识拓扑图和帖子、文章建立映射即可。

IOS7白屏Bug

产生原因:IOS7中,如果html header 返回了cache-control:max-age=0(或者no-cache),这个时候Browser不会cache页面,但浏览器每次请求都会携带请求头(If-Modified-Since或者If-None-Match)。如果此时服务器刚好没有修改的话,会识别成304给Browser,此时前面已提到过Browser没有缓存页面内容,导致取不到页面而白屏。详细Bug描述猛戳这里

解决办法

  •  **设置cache时间(诸如:cache-control:max-age=600):**
     对于首页更新要求来讲,如果有cache的话,不利于首页及时更新,对于更新场景不多的情况,可以采取这个办法
    
  •  **去掉cache-control字段头(我们采取这个方案)**
    
  •  **服务器动态处理:**
    当然对于以上的方案都是基于IOS7,服务器根据请求的Header头中的User-agent来单独针对IOS7及以上处理
    

@herbertliu 这块我感觉另开一个repository,用rm的形式沉淀性会更好,大家也更好修改和提交。
比如:
移动Web前端知识库
iOS与Android平台上问题列表

@litten 他这个已经是总结的了,总结的是基于这个讨论梳理出来的。现有讨论沉淀,然后才有梳理。我们到时候梳理会形成知识图(形成repository)。主要是有些需要讨论的,可以在这里进行一个讨论。

md的确更适合展示,先沉淀到这里吧。后面会整理一下的。 发现at没了yuanyan,战斗力差了好多

Android2.3下transform失效

Android 2.3某些页面的transform失效, 导致rotate等属性无效, 可以通过user-scale=yes部分解决.

腾讯播放器使用时遇到的坑

  1. iOS下单页面里初始化多个video, 会出现神奇的bug. 因此不要初始化多个video
  2. 使用播放器+poster覆盖掉原来的图片(例如课程详情页), 会有一段时间的白屏. 解决方法是oninit并且延时100ms才把播放器展示出来.

[经验沉淀]多页面更新

在移动端比较偏向于新开页面来处理一些事情,另外移动端的操作环境只有返回上一层和home两个操作,因此用户会经常回退到之前的页面,所以往往需要旧页面可以自动更新数据,而产品一般不会提到这些细节,所以需要开发自己保持这种习惯,不然会有很多这个bug和工作量留到测试阶段。目前有两种解决方案:

  • 高版本手Q(5.0)支持WebView的广播事件,可以通过这个进行数据更新,在手Q项目中,对于可以容忍低版本更新问题的可以采用这个方案
  • 利用localstorage进行页面之间的同步

[机型兼容][IOS]type=search输入框会截取字符

是type=search的input的默认样式导致的:
image
解决办法是把默认样式去掉,或者添加padding-left或者text-indent

[经验沉淀]tap点透问题

产生条件

如果绑定tap方法的dom元素在tap方法触发后会被干掉(隐藏,移走,删掉),则它底下同一位置的dom元素会触发click事件、或者有浏览器认为可以被点击有交互反应的dom元素(例如input的focus事件),这个称为“点透”现象。

产生原因

  1. click事件在移动端会有延迟(因为需要检测双击事件,移动端click300毫秒延迟的原因
  2. zepto的tap事件是绑定在document.body上的,tap事件执行(冒泡之后)之前,click事件已经被"执行",只是被延迟了而已,所以在tap事件用preventDefault也无济于事

解决方案

  1. 底下的元素也使用tap事件,即上下两个元素使用同一种事件(tap/click)
  2. 不要使用tap事件,使用touchend事件处理,并preventDefault掉(不优雅,而且暴力)
  3. 使用fastclick库,其实现原理就是把click的300ms延迟干掉

[机型兼容][IOS]position:fixed + input问题

当input获得焦点并弹出虚拟键盘时,页面上position:fixed的元素的位置会错乱。

解决方案

  1. 用position:absolute模拟,这个效果不佳,在pc端hack ie6...只能呵呵
  2. 当input元素focus时,改成position:absolute,blur的时候再改回来
  3. 使用iscroll库
  4. 使用div内滚动

[机型兼容][IOS8]闪屏问题

大面积的页面内刷新时出现,这种闪屏不是位置错乱的那种闪屏,具体原因不详

规避方案

  1. 提高刷新效率,尽量减少reflow和repaint
  2. 刷新之后不要改变页面的scroll状态,即不要从不能scroll刷到可以scroll,反之亦然(利用min-height让页面一直处于scroll状态,在用)
  3. 不要刷新图片,利用localstorage缓存图片链接,命中的图片直接使用src,而不要用lazyload(在用)

[经验沉淀]别忘了点击态

就像pc端的3态,在移动端需要点击态,移动端的点击态实现是点击时添加一个active类,300ms后去掉,建议把点击态放到移动端视觉检查项中

[经验沉淀]页面的各种状态

这些状态pc端也会用到,不过在移动端会更为需要,因为移动端网络延迟高并且不稳定。建议是把页面的各种状态抽象为组件,并整合在一个组件中统一管理会比较好,这个待优化,现总结页面中会出现的各种状态:

  1. 加载js前,需要在html内显示【正在加载】或其它类似标志
  2. 加载js后,调用cgi时,显示loading标志
  3. 如果cgi做了localstorage缓存,在展示local数据同时拉cgi时,显示更新数据标志
  4. 数据为空时,应该展示空标志(不管是cgi返回空结果,还是页面更新数据后变成空结果)
  5. cgi加载失败,显示重试提示,点击后可触发重试逻辑

[经验沉淀]cgi添加localstorage逻辑之后需要注意的地方

  1. 变量重复初始化
  2. 事件重复绑定
  3. 页面状态切换

[机型兼容][IOS 5+]快速回弹滚动问题

IOS5新特性webkit-overflow-scrolling: touch可以启动快速回弹滚动(fast bounce-scroll)效果,但是它会阻止渲染直到滚动结束。

影响

  1. 从不滚动状态到滚动状态(反之亦然),因为要先初始化滚动状态才开始渲染,虽然很短暂,但也是有delay的,所以出现闪屏(公众号2期课程详情页闪屏问题)
  2. 列表滚动过程中,需要等到滚动结束之后,后面的元素才会渲染出来(公众号2期课程列表页下拉刷新元素出来慢的问题)

解决方案

  1. 启动硬件加速,可以用-webkit-transform: translate3d(0,0,0),这个hack可以解决大部分问题
  2. 用min-height,直接杜绝滚动状态改变,从而防止闪屏问题(已解决详情页闪屏问题)

ps: 【[机型兼容][IOS8]闪屏问题】的根本原因

[机型兼容][iOS] 部分图片不缓存

经过排查, 图片大于1M的情况下, 并且开启了crossOrigin, 就会导致浏览器不缓存该图片.

解决方法是:

  1. 图片大小减少
  2. 去掉crossOrigin

@ousiri 图片不缓存,有哪些机型有问题?

iOS~~~~

Listview 太长引起的手机性能问题

内存中存留的DOM结构太多,导致滚动的 Listview 后面,点击响应会延迟,甚至无响应。

解决方法是:

  1. 在 li 外面包裹一层,将前面页码的 dom 移除,同时设置外层容器的高度(这样不至于影响滚动条)
    2015-06-14 10 15 53

  2. 下拉滚动翻页过程中,对之前页码的数据进行隐藏。
    qq 20150618095357
    向上滚动时,采取一定的策略将隐藏的数据显示

            var $lastHidden = $teacherList.find('li[data-show="hidden"]').last(),
                lastHiddenPage = $lastHidden.data('page');
            var $midle = $teacherList.find('li[data-page="' + (lastHiddenPage + 2) + '"]').eq(4);
            if($midle && $midle.offset() && $midle.offset().top > $(document.body).scrollTop()){
                // 页面中最后一个元素显示在屏幕中
                // problem: 向上滑动过快,这里有卡顿
                $teacherList.find('li[data-page="' + lastHiddenPage + '"]').css('visibility', 'visible').data('show', 'visible');
            }
    

获取最后一个隐藏的元素,得到隐藏的页码,判断后2页中的第5条数据是否在屏幕中。

弹出框中的滚动事件冒泡导致body也滚动

qq 20150731154952
如图所示,当弹出框内容在滚动时,如果滚动到边界,会导致页面内容也会跟着滚动。

  • _尝试解决但失败的方案_:
    (使用zepto)在弹出的对话框元素上捕获事件,并阻止冒泡(stopPropagation),虽然阻止冒泡后,在body上没有监测到touchmove事件,但是页面内容还是会被带着滚动。
  • 成功解决方案一
    在显示对话框时,将html和body的height都设置为100%,overflow都设置为hidden,然后在对话框关闭时将html和body的height与overflow属性都设置为auto。
    打开对话框和关闭对话框后分别执行的函数:
    function lockBody() {
        $body.css({
            height: "100%",
            overflow: "hidden"
        });

        $html.css({
            height: "100%",
            overflow: "hidden"
        });
    }

    function unlockBody() {
        $body.css({
            height: "auto",
            overflow: "auto"
        });
        $html.css({
            height: "auto",
            overflow: "auto"
        });
    }

这种方案最先想到,但这种方案比较麻烦,需要使用js在对话框显示与关闭时更改元素的css,性能会受影响。

  • 成功解决方案二
    在body内加一层div.scroll-wrapper,这个div包含页面的所有显示内容但不包含弹出框,.scroll-wrapper和html还有body的height都为100%,html和body的overflow为hidden,.scroll-wrapper的overflow为scroll。让.scroll-wrapper的div来控制页面内容的滚动。因为弹出框是通过fix布局不属于.scroll-wrapper的子元素,所以滚动不会冒泡到.scroll-wrapper上。
    DOM结构:
<body>
    <div class="scroll-wrapper">
        <div class="banner">
            banner
        </div>
        <div class="content">
            <p>p </p>
        </div>
    </div>
    <div id="mask">
    </div>
    <div class="dialog">
        <div class="dialog-head">

        </div>
        <div class="dialog-body">
            <ul>
                <li>aaaaaaaaa</li>
            </ul>
        </div>
        <div class="dialog-foot">
            <a href="javascript:;">确定</a>
            <a class="close" href="javascript:;">关闭</a>
        </div>
    </div>
    <script src="zepto.js" ></script>
    <script src="index.js" ></script>
</body>

CSS:

.scroll-wrapper {
    height: 100%;
    width: 100%;
    overflow: scroll;
}

html, body {
    height: 100%;
    overflow: hidden;
}

这种方法不需要使用js逻辑来控制,只用加一个div元素并设置相应的css进行控制。

测试源代码:https://github.com/coolriver/webTest/tree/master/mobileH5/dialogScroll

我是用的iscroll解决的上述问题

@chshouyu iscroll模拟滚动,自然就不会有这个问题了,只是iscroll在很多android机型下有性能上的影响,而且在ios下不如原生的流畅,所以除非是必须要用iscroll(比如:需要监听滚动事件等),才会将原生滚动换成iscroll。

这两种解决方案在ios上表现都不太好,ios上滚动没有惯性动画了啊。感觉这种方法搭配iscroll,根据操作系统选择使用的方法更好些?我最后用的也是iscroll

@banyaner 在实际业务中,确实需要根据场景做一些不同的处理。比如说浮层的场景,整个dom的场景都不一样,根据场景不同。另外不同平台的考虑也需要。总之,方案有这些,需要不同场景选择合适的方案。

Android手Q的X5内核webview使用两个方向原生滚动会导致页面截断

如下图,页面存在这window的垂直滚动,和红色框的纵向滚动
image

红色框是使用原生的overflow-x:scroll
image

然后
向下滚动一部分页面,然后左右滚动红色区域的地方

image

接着向上拉,滚回原来的头部,,页面出现截断
image

[疑难问题]IOS 中微信浏览器中滚动事件触发多次,导致数据重复加载。

   //滚动事件(安卓没有触发多次,IOS中一次滚动会触发多次事件)
        $(".contain").scroll(function () {

            if ($(this).scrollTop() + $(this).innerHeight() > $(this)[0].scrollHeight - 300
			&& mySpace.oldScrollHeight < $(this)[0].scrollHeight) {
				mySpace.oldScrollHeight=$(this)[0].scrollHeight;
                console.log(mySpace.currentTabId + " scroll to bottom");
                mySpace.getDataByTabId();
				//alert("scroll to bottom"+mySpace.oldScrollHeight);
            }
        });