本次初步讨论两个问题:
- Native 卡顿
- 网页卡顿
Native卡顿
iOS客户端卡顿已经是老生常谈的问题了,解决方案各有千秋,大概做一个总结,不确定全部包括。
iPhone的屏幕刷新频率是60FPS,即16ms一次,如果在一次渲染周期内无法将当前的内容渲染完成就会出现掉帧的情况。App 主线程在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区。iOS显示系统有两个缓冲区,即双缓冲机制。在这种情况下,GPU 会预先渲染好一帧放入一个缓冲区内,让视频控制器读取,当下一帧渲染好后,GPU 会直接把视频控制器的指针指向第二个缓冲器。
当视频控制器还未读取完成时,即屏幕内容刚显示一半时,GPU 将新的一帧内容提交到帧缓冲区并把两个缓冲区进行交换后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂现象。为了解决这个问题,GPU 通常有一个机制叫做垂直同步(简写也是 VSync),当开启垂直同步后,GPU 会等待显示器的 VSync 信号发出后,才进行新的一帧渲染和缓冲区更新。这样能解决画面撕裂现象,也增加了画面流畅度,但需要消费更多的计算资源,也会带来部分延迟。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。
常见解决方案
- UIImageView尽量设置为不透明
- UIView的背景色尽量不要设置为clearColor,这样也会触发alpha叠加
- 最好不使用带alpha通道的图片,如果有alpha尽量让美工取消alpha通道
- cell上layer尽量避免使用圆角(避免离屏渲染等)
- 尽量延迟图片的加载(布局特别复杂的cell,滑动时尽量不要加载图片)
注:严格意义上 self PerformSelector 的事件就是在主线程队列中等待。 - 适当使用预加载、预渲染等
- 让图片的绘制、图片的下载、对象的创建、文本的渲染等这些耗时的操作尽可能采用子线程异步的方式去处理(如ASDK)
- Storyboard和XIB因为初始化对象分配内存先后的问题会导致CPU占用大于纯代码
其实吧,我觉得主要还是分人,有些开发用起东西来就是费,完全不考虑成本。不过现在的机器的性能也确实好一些,一些小问题倒也看不出来,但这绝不是偷懒的借口。从小处着手,把每一行代码写好点,很多问题就解决了。
网页卡顿
网页出现掉帧主要就是因为事件触发过于频繁,网页来不及处理导致在下一个事件被触发之前无法完成,特别在scroll resize这样的事件同时又涉及大量DOM操作和元素绘制的时候,网页掉帧就会比较严重。
一个页面被呈现在用户面前之前,通常会经历JavaScript代码的加载和解释、计算样式、布局(layout)、为DOM元素填充像素(paint)、合并渲染层(DOM元素的绘制是在多个层上进行的,绘制完成后再进行合并)。具体过程可以参考之前写的浏览器的构成,浏览器内核,仅供参考。
页面生成的时候会被至少渲染(layout+paint)一次,在被访问过程中也会不断地重排和重绘,特别是scroll resize的频繁触发会导致浏览器不停地重新渲染页面,通过元素的分组当某个层内容改变的时候,就可以仅仅重绘这个层,不需要整个页面重绘。也可以通过防抖函数,减少事件在一定时间内的触发次数,这样对网页展示会有很好的提升。
结合防抖,采用节流函数,只允许一个函数在X毫秒内执行一次,只有当上一次函数执行后过了规定的时间间隔才能进行下一次该函数的调用。效果会更好一些。
前端需要注意的地方
- 尽量减少layout
- 简化事件内操作,将一些变量的初始化,不依赖于位置变化的计算等都当在scroll外提前就绪
- 滑动过程中尝试使用pointer-events:none 禁止鼠标事件可以提高滚动时的帧频(使用后hover click事件会全部失效,要在停止滚动的时候移除掉这个属性)
- 简化DOM结构
- 适当的开启硬件加速
- 触发view前准备好数据,做好缓存
客户端能做的事
- 使用WKWebView 替换 UIWebView
- 合理实现页面缓存技术(预加载Web页面, NSURLCache、NSURLProtocol )
- 采用Hybrid等技术
- 网页转成Native 等