最近兄弟团队给提了一个bug,说他们iOS端的网页,触摸后很大几率出现白屏,一开始我是很不信的,后面本地调试竟然更高概率发生。

首先,系统是iOS10、11,网页是高度100%,中间部分越界滚动的一个常规效果,框架vue,不过感觉bug和vue关系不大,大概结构如下:

<body>
	<div id="app">
		<header class="page-header"></header>
		<div class="page-content"></div>
		<footer class="page-footer"></footer>
	</div>
</body>

由于希望得到平滑点的滚动,在中间滚动部分加了

.page-content {
    -webkit-overflow-scrolling: touch;
    overflow-x: hidden;
    overflow-y: auto;
    ...
}

本来表现很正常的东西,突然出现白屏,而且是页面触摸就白屏,测试了好几部手机,个别是白屏后一直白着,个别是白屏后过半秒左右又自动恢复正常,有些不解,但看样子有点像是重绘或者重排导致的。

网上找到类似介绍如下:

webkit在绘制页面时会将结构分为各种层,当层足够大时就会变成很大的平铺层。这样一来webkit在每次页面结构发生变化时不需要都渲染整个页面而是渲染对应层了,这对渲染速度来说相当的重要。webkit会给各种层分配一定大小的“后备存储器”在内存里缓存起来,这就是绘制层的上下文,通过这个上下文就可以很容易的实现各种效果(动画,3D变换等),“后备存储器”内存占用大小不仅依层而定,跟设备和显示方式也是有关的,假设这在普通屏幕下是1:1的,但在Retina屏幕下则是1:2的,并且放大时这个量会成倍增加;一张图片是10X10,普通屏幕分配的就是10X10,Retina初始则是20X20。这也表明Retina是更加消耗内存的。当层很大时,意味着“后备存储器”会消耗更大的内存,为了避免这点,webkit并不会绘制一个很大的层来存储一个很大的页面,比如说平铺层则会拆分成很多的块来绘制,即尽占用尽可能小的内存,只是将可视范围内的那部分渲染出来。这就是为什么我们在大页面滚动时会发现下面的内容慢慢显示,向上滚动时上面的内容还慢慢显示的原因。

以下则是webkit划分为层绘制的场景:

1、页面主容器永远是独立的平铺层
2、绘制密集型元素时,如<video>, <canvas>
3、应用3D transformations的元素,包括translate3d, rotate3d, translateZ
4、内容被加强时,如Filters, masks, reflections, opacity, transitions, animations
5、某些特殊的情况下也会,如position:fixed, -webkit-overflow-scrolling:touch
6、任何在已知层上覆盖的内容

同时,还发现我在中间部分设置了-webkit-overflow-scrolling之外,body元素也加了该属性,并且也有overflow-x: hidden,因为body和中间部分明显不在一层,猜测会不会是两层触发分层绘制时时机错开了,抱着试试看的态度,把body上的-webkit-overflow-scrolling、overflow-x: hidden移除,刷新页面,竟然可以了,而且经尝试,overflow-x: hidden改为overflow: hidden一样没有问题。

不过有文章提及,body设置了-webkit-overflow-scrolling,整个文档都会生效,而且继续测试中发现,无论是移除body上的-webkit-overflow-scrolling还是overflow,都可以解决问题,考虑到内容都在body下面作为中间层的div中,为了方便,这里直接将body上overflow移除,保留-webkit-overflow-scrolling,并且要求团队兄弟不再在body以外的地方使用-webkit-overflow-scrolling,暂时解决了问题。具体根源还没找到更权威的解释,暂且记录一下,如有更深层的解释,欢迎补充。

参考文章:http://www.iunbug.com/archives/2012/09/19/411.html