店铺后院优化小记
不知道大家听说过淘宝店铺后院了吗?没有的话,可以先逛一下:
御宅伴侣: http://homemate-uk.taobao.com/hy/index.htm
绘本家居: http://huibenjiaju.taobao.com/hy/index.htm
绿普洱: http://ttdmg.taobao.com/hy/index.htm
小王子: http://astprince.taobao.com/hy/index.htm
巴酷: http://mycoffee.taobao.com/hy/index.htm
剪裁时间: http://jiancaishijian.taobao.com/hy/index.htm
阿芙家: http://shop59240413.taobao.com/hy/index.htm
宣唯服饰: http://xuanweifs.taobao.com/hy/index.htm
悦茶集: http://yuecha.taobao.com/hy/index.htm
芙熙: http://fu2home.taobao.com/hy/index.htm
未禾: http://shop34187315.taobao.com/hy/index.htm
木梓: http://muzi-collection.taobao.com/hy/index.htm
贝尼尼: http://aichaojia.taobao.com/hy/index.htm
张于龙: http://zyljc.taobao.com/hy/index.htm
以美: http://emelytea.taobao.com/hy/index.htm
乐品: http://fift.taobao.com/hy/index.htm
店铺后院上线初期遇到不少性能问题,主要体现在几个主要的动画 CPU 占用率比较高,整体感觉并不流畅,尤其在 Pad 端体验并不是非常好。
在和 云谦 讨论过后,首先决定用 CSS3 的 Transform 加 Transition 实现主要动画,这样的好处是在大部分机器的高级浏览器上能自动使用 GPU 硬件加速,降低 CPU 损耗和提高动画流畅度 [1] [2] 。本文也主要讲这部分遇到的一些问题。
一、Webkit Transform 动画闪动
在 黑三 的 《Chrome渲染Transition时页面闪动Bug》有提到过,Transition 实现的 Transform 动画会导致 Chrome 闪动,这个问题在新版的 Chrome 上已经修复,但在 iPad 的 Webkit 浏览器上,这个问题依然存在。
解决方法:与 黑三 的解决方法一致,只需要加上样式 -webkit-backface-visibility: hidden;
二、Transform 带来的定位问题
我们把主要动画用 Transform 实现后,发现在大多数浏览器下 position: fixed 定位失效了。找到 W3C 关于 Transform [3]有这样一段话:
In the HTML namespace, any value other than ‘none’ for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.
也就是说,使用了 Transform 的元素类似于添加了 position: relative,并且里面 position: fixed 的元素将相对于这个元素定位,而非原来的浏览器窗口。但这样的实现似乎还存在争议,网上大多数搜索结果都把这个行为看作是浏览器自身的 bug,而且 IE9-10(是的,有且仅有 IE),在 Transform 里面使用 position: fixed 的参考对象还是浏览器窗口。
解决方法是在 Transform 内部避免使用 position 定位, 如果一定需要 position: fixed,那只能弃用 Transform,或者如果动画终止状态是原状态,可以在动画完成后添加 transform: none
DEMO在此: http://jsbin.com/upataw
三、overflow: scroll 里惯性滚动(Momentum Scrolling)的实现和带来的渲染延迟问题
移动端大多数浏览器都支持溢出元素的内部滚动 [4]。而且在 Webkit 浏览器里,可以用 -webkit-overflow-scrolling: touch 使这个滚动像页面滚动一样有惯性和加速度。
但在大页面中,加了这个样式后会导致滚动后的内容渲染延迟十分严重,导致体验非常糟糕。解决方法倒相对简单:给滚动的子元素强制 GPU 加速加快渲染效率。一般用 TransformZ 在元素没有动画时强制使用硬件加速(即 el > * transform: translateZ(0)),当然,要注意上面一、二点提到问题。
DEMO在此: http://jsbin.com/idizec/32
四、脚本控制 Transition 动画容易丢失动画过程
这是一个老问题了,只不过在实现 Transition 动画时更容易重现。一般实现点击后出现 Transition 动画,会通过预先定义好样式,再 addClass 到元素上触发。但这种操作 DOM 的行为一旦在脚本一个时间片内出现多个时,前面的动画效果可能会被合并,直接“跳”到动画的最终状态或只执行最后一个动画,这是 javascript 引擎的线程机制导致的。
解决方法是强制将重要的 DOM 的操作,如触发动画的行为,放入独立的堆栈,保证这个操作在独立的堆栈执行。setTimeout 和 KISSY 中的 .later() 是最常用的方法,即使延迟时间设为 0s ,执行函数也会放到下一个独立的堆栈,即在下一个时间片执行。 关于 javascript 的线程机制,可以 参考这里 [5]
五、首屏资源加载影响渲染性能
这是一个还在研究的问题,在 Chrome 下(其它浏览器未严格测试),首屏资源如果还在加载中(即使非阻塞),onload 事件触发之前,页面渲染性能会严重下降。这里的首屏资源是包括一定时间内的异步加载资源,也就是在某个间隔时间内的异步请求也会被浏览器识别为首屏资源,表现就是 Tab 上的 loading 图标还在,此时页面渲染性能非常低,滚动时能感觉到明显的拖滞感。(有些时候甚至是别的 Tab 正在 loading 也会影响)
这里的 DEMO http://jsbin.com/idizec/33/edit,只模拟了「在某个间隔时间内的异步请求也会被浏览器识别为首屏资源」的情况,你可以发现即使 setTimeout 延迟了 200ms 加载一张根本不会出现的图片,也算在了首屏加载里面,要等待图片加载完成,onload事件被触发,Tab 上的 loading 图标才会消失。
在开发者工具中的 Timeline – Frames 可以看到,首屏加载时会有大量冲到顶部的空白条形,而加载完成后基本稳定在 60 fps。(空白条形初步猜测是浏览器为了同步帧率而产生的)
因为这个问题多少涉及网络原因,所以解决方法我们只能从尽量减少在首屏和后面短时间内不去加载非必需的内容,如压缩减少资源大小、图片懒加载、外部组件延迟调用。
对于后院整体优化,我们遵循了一些简单的原则:更少的事件监听、善用事件委托、及时释放无用 DOM 和事件。另外为了占用最小化,对于一些组件我们也自己写了一些更简单的模块而没有使用 KISSY 现成的通用组件。
其它的,更多是一些细节问题,如 无论是否可视 gif 动画会一直消耗渲染性能得及时释放、 Firefox 中有更严格的 CSS3 语法( transition: all 1s ease 0s; 而不是 transition: all 1s ease 0;)等等。另外我们也会从脚本逻辑去考虑优化视觉流畅性,如动画完成后再执行大量操作的脚本。
或者再举个例子,在横向瀑布流中,因为每个 Feed 都有自身绑定的一些鼠标触发的事件和 CSS Hover 属性,滚动过程中就会不断触发鼠标所经过的 Feed 的事件和 Hover 样式,十分影响滚动过程的渲染效率。最初的想法是在滚动时禁止除鼠标滚轮意外的所有默认事件,可是后来发现 CSS Hover 并不能被禁止,最后想出一个简单粗暴的办法:滚动时覆盖一个透明层在瀑布流上面 — 至少也能有效阻止滚动时鼠标触发 Feeds 上的事件。
总结到此,因为示例较少,欢迎大家逛逛淘宝去发现更多已开通后院的店铺。
感谢:
- 美好的 Internet Explorer 6
- 负责了大部分优化工作的 云谦
- 技术强悍的 前端店铺组
- 有点重量的 后院项目组
引用:
[1] IMPROVING THE PERFORMANCE OF YOUR HTML5 APP - http://www.html5rocks.com/en/tutorials/speed/html5/#toc-hardware-accell
[2] Why Moving Elements With Translate() Is Better Than Pos:abs Top/left - http://paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
[3] The Transform Rendering Model - http://www.w3.org/TR/css3-transforms/#transform-rendering
[4] Overflow scrolling - http://barrow.io/overflow-scrolling
[5] setTimeout与js引擎的异步执行 - http://lijing00333.wordpress.com/2011/02/08/settimeout%E4%B8%8Ejs%E5%BC%95%E6%93%8E%E7%9A%84%E5%BC%82%E6%AD%A5%E6%89%A7%E8%A1%8C/