兼容篇
兼容篇是我最想写的一部分,在这之前也总结过很多关于移动开发的兼容问题与解决方案。对于移动Web开发来说,兼容是开发重心,通常要花费30%甚至更多的时间去处理一些兼容问题,甚至时间花掉了,问题依然无法解决。
相比PC Web开发,移动开发的兼容性需要考虑的问题更复杂,我自己花了一个图:
在上图中,我列了两个维度:标准支持和个性化,我觉得这两个维度能很好的体现PC与移动在兼容方面的差异。
PC Web开发中,更多时候是在处理对标准支持程度的兼容问题,问题类型相对比较单一。在进入到IE9+时代,对HTML5特性与其他标准支持已经越来越好,前端工程师已经获得了很大的解脱,可能描述得有一定的片面性,但差不多就是这样。
反观移动端,还处于初级阶段,相比PC浏览器来说,移动浏览器对标准的支持起步较高,从Android2.1+就已经支持了很大一部分的HTML5基础功能,对ECMAScript 5的支持更是好,但这并不代表移动端的兼容性就非常的好处理。如上面那张图,移动浏览器的个性十足,各个厂商都有自己特有的东西,这就给移动开发的兼容性带来了十分大的挑战。
移动Web开发的兼容问题主要可以从以下几个层面来理解:
- 设备差异导致的兼容问题。
- 操作系统差异导致的兼容问题。
- 浏览器差异导致的兼容问题。
这三种差异,依次是设备 < 操作系统 < 浏览器
,越接近应用层,需要处理的兼容问题越多。
设备差异
可能有些人不解,为什么设备差异会导致兼容问题呢?其实在前面的交互篇就已经有提到移动设备的物理属性的差异,这就直接导致了在移动Web开发的兼容问题。比如有些比较老旧设备的触摸屏是不支持多点触控的,只能捕获单点,这就使得一些需要多点触摸的应用场景出现兼容问题,比如图片的放大、缩小。类似这种硬件缺胳膊少腿的问题是硬伤,无法解决,一般遇到了也只能忽略,但可以考虑给用户一个提示。当然时代在变化,而且变化得很快,这样的手机毕竟只是少数,就像抛弃IE6一样抛弃它吧。
另外一个问题就是devicePixelRatio(设备像素比)的问题,在前面视觉篇分享过两篇文章,可以拿到这再分享一次:
- @张鑫旭 的文章《设备像素比devicePixelRatio简单介绍》
- @大漠 的文章《走向视网膜(Retina)的Web时代》
devicePixelRatio主要影响的是图片显示的问题,devicePixelRatio的发展有两个趋势:
- 老旧设备,devicePixelRatio较低,比较奇葩的
devicePixelRatio==1.5
这样的值,可能导致图片显示锯齿; - 新设备,屏幕分辨率在往2k时代发展,
devicePixelRatio==3
甚至大于3,所以图片显示可能会比较虚;
在没有特殊要求的情况下,一般移动端是不对图片做特殊处理的,统一都是使用2x
大小的图片。处理时一般分为两种情况:
直接引用图片<img src="xxx.png">
,直接使用2x
大小的图片,将图片高宽设置为物理像素的1/2既可。
另一种情形就是最常见的背景图,将background-size
设置为物理像素的1/2。比如图片物理像素为200*200,代码如下:
<style>
#b1 {
100px;
height: 100px;
background: url("xxx.png") no-repeat;
-webkit-background-size: 100px 100px;
background-size: 100px 100px;
}
</style>
<div id="b1"></div>
如遇到一些需要背景图自适配宽度的应用场景,可以使用background-size: cover;
。
操作系统差异
操作系统差异与浏览器差异是会产生重叠知识点,操作系统差异更偏向于描述由于iOS、Android或其他系统差异导致的兼容问题,而浏览器差异偏向描述由于不同浏览器厂商导致的兼容问题。
相比硬件设备来说,操作系统也给移动开发带来了不少麻烦,在前面视觉篇的最后已经提到一个字体兼容的问题,参见:@元彦 给出了解决方案 《字体设置最佳实践》。
另外一个比较经典的问题,在Android下orientationchange
事件回调,需要延迟一点时间触发回调函数,才能获取到正确的window.innerWidth
和window.innerHeight
的值,在iOS是不需要做这个延迟的。分别使用iOS与Andorid设备访问 DEMO,横竖屏查看对比结果。
我一般会将Android设备的回调延迟300ms处理,代码如下:
var isAndroid = /android/.test(window.navigator.userAgent.toLowerCase());
function createOrientationChangeProxy(fn) {
return function() {
clearTimeout(fn.orientationChangeTimer);
var args = Array.prototype.slice.call(arguments, 0);
fn.orientationChangeTimer = setTimeout(function() {
var ori = window.orientation;
if (ori != fn.lastOrientation) {
fn.apply(null, args);
}
fn.lastOrientation = ori;
}, isAndroid ? 300 : 0);
};
}
window.addEventListener('orientationchange', createOrientationChangeProxy(function() {
if (window.orientation == 0 || window.orientation == 180) {
resizeIcon();
}
}), false);
但其实,即使延迟300ms也不能解决所有Android设备的问题,比如我在小米2原生浏览器上需要延迟800ms才能获取到正确高宽。我想这与小米浏览器的内部实现有关,一些浏览器在进入全屏状态收起地址栏时,会执行一个收起地址栏动画,这个动画大概要消耗500ms左右的时间,这个动画并不是所有浏览器都是这么实现的,所以,我想可能是因为这个原因导致有些浏览器需要更长时间才能获取到正确的高宽,也可能是我猜错了。
另外,我还在三星手机上遇到过一些更奇葩的问题,使用JavaScript处理animation动画时,设置动画属性。比如:transform
或animation
时,必须使用-webkit
前缀的属性,如果没有这个前缀,在三星手机原生浏览器下,动画会失效,而在其他浏览器下使用前缀的属性是OK的。
操作系统差异与浏览器差异,没有一个完整的边界,产生的问题也模拟两可。如果在所有Android设备上都出相同的问题,这样的问题都可以认为是操作系统差异,一般这类问题都是由于Webkit内核或系统底层的一些原因导致。
浏览器差异
对于移动Web开发来说,我们处理的70%兼容问题都会是浏览器差异导致,在这里我不会总结浏览器差异的兼容问题,因为实在太多,阅读过 @jtyjty99999 mobileTech项目 就能体会到,那个篇幅,那个长度,简直无力吐槽。可以这么说,只要浏览器版本在更新,就会不断有新的兼容问题产生,一切浏览器个性化功能和对底层引擎的优化都是兼容问题的罪恶之源。
但是,要肯定一点,移动开发兼容一般只需要做两种浏览器的兼容:Webkit内核与IE的Trident内核。如果不考虑WP用户的话,更是连IE兼容都可以省了,所以写代码时,都优先考虑-webkit
前缀的兼容,这点是移动Web开发的优势。
浏览器的个性化
在iOS中,苹果强制所有浏览器必须使用自家的Webkit内核,所以所有浏览器的体验非常一致,出问题的话也都出一样的问题,改问题也比较容易点。幸运的是iOS把它优化得十分不错,所以更多的程序员都喜欢做iOS下的前端开发。
相比iOS,Andorid可谓是百花齐放,各个浏览器都个性十足,比如UC浏览器,它包含很多功能:
- 极速模式
- 自带手势,比如:左右手势滑动切换页签
- 无图模式
- 云加速
- 网页预读
- 广告过滤
- 夜间模式
- 等等
每个浏览器优化的功能都是一个陷阱,都可能导致应用无法正常工作。最常见的是浏览器自带的手势与页面手势冲突,导致页面手势无法正常工作,又或者浏览自带一些悬浮按钮对页面上的按钮产生了遮挡等等。
一些不太容易遇见的异常就可能与应用场景有关。比如我之前做的一个功能,在页面初始化时需要使用JS记录页面运行一些状态参数,由于UC浏览器的网页预读功能,它会自动识别当前页面的下一页
按钮,然后预读下一页并且提前渲染与处理JavaScript。这就导致页面状态记录错乱,因为在用户没有真实看到下一页时,实际上状态参数已经改变为下一页的状态了。这个问题最后是在下一页
按钮上加上了disabled
样式解决的,如果<a>
有disabled
样式或属性,那么UC在预读时就会忽略掉。
再比如 APP下载链接被当作广告过滤掉等等,类似这样的问题还有很多,都是因为浏览器自身特性导致的兼容问题。
浏览器实现差异
浏览器差异除自身功能特性之外,还有很多实现差异导致的兼容问题,这样的问题是最坑爹的。为什么说坑爹呢?看下面几个例子:
软键盘
- 在一些Android原生浏览器下,软件盘弹出之后,页面内容会遮挡,并且页面也无法滚上去;
- iOS浏览器也会有自己的软键盘弹出的问题,参见《移动端web页面使用position:fixed问题总结》
文本框
- 在一些Android浏览器下,文本框focus被激活时,层级最高,会挡住所有页面元素(包括fixed元素);
- 这种遮挡问题同样在Android一些浏览器的
<video>
标签中也有体验,比如UC或QQ;
position:fixed
- 以前写fixed元素时,总是能遇到各种问题,比如:在一些Android浏览器下滚动页面,fixed元素跟着页面滚动不置顶。
- 参见我以前写得一些文章 ** 《移动端web页面使用position:fixed问题总结》 ** 《移动Web开发实践——解决position:fixed自适应BUG》
前些天又总结了一篇博文《微信内置浏览器WebApp开发,踩坑》也是有关浏览器踩坑的问题。
这些在正常情况下,都认为没有问题的地方,往往都会出现各种奇葩的兼容问题,而正是这些问题耗费了我们大量的精力和时间。
小结
现在回过头看以前遇到的一些兼容问题,随着Android版本、Webkit引擎和各种浏览器的升级,很多兼容都在从底层被解决,并且对标准的支持也越来越好。
以前我老是会纠结是否需要去解决一些老旧设备遇到的兼容问题,经过这么长时间,对这个问题我也找到了一个自己的答案:“应该大胆的使用更高级,能带给用户更好体验的特性,不要顾虑太多,移动技术发展得太快,快到等你做完这些兼容之后,这些设备已经被淘汰了。”
和大家分享一组内部的统计数据:采样12万个,Android系统,Andorid 2.x 占15.47%,Android 4+ 占82.12%。
更正
在视觉篇中提到的:维基百科中List of displays by pixel density详细的记录了各类设备的尺寸和分辨率。
更正:维基百科页面被删除,原因不明。使用两个替代方案查询设备的尺寸和分辨率: