渠宏伟 企鹅电竞前端团队Leader
H5页面存在的问题
H5页面对比终端的不足,第一,加载慢;第二,交互差。
加载耗时比较长,因为它受限于Webview,Webview在Android上启动就比较慢,另外页面资源加载慢。我们很多业务页面都是用H5做的,长列表滚动和侧滑操作存在明显的卡顿,经过持续优化也很难和原生界面媲美。
H5极速加载方案 ——VasSonic
加载慢的问题,我们实现了H5极速加载方案 —— VasSonic,加载速度实现秒开,在手机QQ上广泛应用。
- WebView和页面并行加载
- 资源离线预推
- 页面局部刷新
- 动态缓存
H5页面首屏实现秒开 (https://github.com/Tencent/VasSonic)
Weex实践和收益
为什么选择Weex
- 企鹅电竞正在快速发展中,我们需要一套兼顾性能与动态性的技术方案来支撑企鹅电竞产品日益丰满的业务需求,weex是一个不错的选择。
- weex选择了vue作为上层框架,代码编写体验和使用vue差不多。企鹅电竞前端团队的许多项目之前也选择了vue作为开发框架,小伙伴们可以快速上手。
- facebook的专利许可证风波让开发者们人心慌慌,不敢再使用他家的React Native。 weex借鉴了React Native的思想,是一种不错的RN替代方案。
我们选择了Weex作为原生渲染方案,它的主要特点:
- 前端页面原生渲染,增强前端页面体验
- 一次开发支持三端(Android、iOS、H5)运行,提高开发效率
在企鹅电竞业务当中大量使用了Weex,如果想体验一下可以下载我们的APP,可能你都看不出来哪部分是原生做的,哪部分是Weex做的,它是一个融合的方案。
获得收益
- 内存降低 45%,帧率提高 15.7%;打开耗时下降 44.9%
- 开发效率比终端提升 40%,有效释放终端开发人力
- 页面更新不依赖版本发布,特性发布效率提升
构建流程改造
我们接入时首先做了一个构建流程的改造。这是Weex官方构建图,用一个公共APP进行webpack打包。我们的应用比较复杂,往往在H5上有一些特殊的逻辑,或者在原生上有一些特殊的逻辑,如果是统一写在app.js里,就会造成两边打出的js有冗余。
我们就做了一下改造,其实改了页面的开发方式。除了app.js之外,又分成两个入口,一个是对web的,一个是对Weex的。
分入口再引用相同的app.js,可以让web版本和weex版本保持相同逻辑,也可以独立扩展,互不影响。
接头暗号
头部的{"framework":"Vue"}告诉解析器用Vue的语法解析JSBundle。如果删除了这行注释,将会白屏。
Uglify压缩会删除注释,导致”接头暗号”被删除。压缩必须放在插入framework之前
调用终端接口
我们的APP接入Weex之前是很成熟的APP,有大量jsapi,如果全部改造成module工作量太大,做法是统一做一个module转发,直接传过来,由module统一解析转发到对应的逻辑,底层jsapi的解析和逻辑保持不变。这样的话,我们的Weex页面直接就集成了原来所有Webview的jsapi能力,是一个通用的。并且以后开发新的接口时,只需要开发一份。
支持cookie
H5中我们使用document.cookie可以获取浏览器的cookie信息。
在weex中如何实现?我们利用weex提供的扩展module的能力,在终端中扩展一个获取cookie的方法。
接口请求支持cookie
- iOS基于UI webview共享cookie。iOS天然支持请求带上cookie
- android需要终端支持,使用cookieManager管理cookie信息,拦截请求自动添加上cookie
支持userAgent
H5页面经常使用UA判断APP环境和版本号,为了兼容H5的逻辑,我们在Weex中支持了userAgent,在weex.config.env添加userAgent属性。 需要注意的是,从weex实例中获取的userAgent是静态的。一旦weex实例创建就固定下来,终端修改UA也不会跟着变化.
如何实现1px
weex px问题是一个坑,定义成了自动缩放单位,颠覆了前端对px的理解。我个人认为,微信小程序的方案是对的,定义一个新的单位rpx实现自动缩放,不影响原来px的实现。
在我们的业务场景中需要使用weex实现titlebar。在宽屏手机titlebar会被缩放,跟终端的titlebar体验不一致。通过动态计算对应的px,重新设置样式,实现和终端一致的体验。
横屏适配
设备宽度deviceWidth是固定的,横屏状态下设备宽度为手机长度,并不是我们的view宽度。看上面的公式,我们可以前端修改viewport宽度750。
适配后就如下图所示效果。注意,meta.setViewport执行时机比较晚,如果希望一开始得到准确的渲染宽度,需要终端同学帮忙weex初始化时设置viewport。
:class 语法限制
点击态
项目比较常见的点击态多半是透明度的变化,如按钮、列表、链接等,css的做法是添加伪类 (:active),weex中也同样支持,但是weex需要在原样式中添加 opacity:1,否则点击后回不到初始状态;
此外,:active使用时,background-image在ios下会失效。
文本截断
文本从限制1行到不限制可以用lines:0
圆角抖动问题
圆角按钮会先显示直角再变成圆角,出现明显的抖动,问题存在于android下 weex sdk
发现weex sdk代码中做了64ms延时造成的,配合在新版本weex sdk解决了这个问题
终端crash问题
Android
- OOM
- PaintDrawable在API 21下偶现渲染crash
- Bitmap回收后继续使用导致crash
- instance被销毁后调用Toast的NPE问题
- WXThread.secure反射调用异常
iOS
- 未显示禁用estimatedRowHeight导致
- tableview在更新数据源时偶现crash
- 布局时node->get_child返回空指针导致crash
- text component渲染和测量高度时偶现crash
Android接入Weex crash率 0.13% -> 1.01%,我们遇到的这些crash问题在企鹅电竞已经解决。
自动化测试方案
weex sdk生成的终端view中的id是自动生成的,自动化测试系统无法识别。
我们借助无障碍化ariaLabel属性作为原生view自动化测试标签。
性能优化
数据缓存优化
1、使用storage缓存接口请求数据
2、优先使用本地缓存数据,同步请求更新缓存数据和页面
jsbundle预缓存优化
1、在现有的 weex 页面配置文件的基础上增加一个字段 preload,当此字段值为1时候,对jsbundle文件进行预缓存。
2、提供js api : biz/preload 提供给前端进行对下一个页面的预加载。biz/clearProload 用于前端对于既有缓存内容的清空.
3、对于缓存内容,使用LRU的算法进行管理,控制缓存总大小为20M。
4、对于使用缓存的内容,终端在ua上添加一个字段 PRELOAD/1 ; 1表示是预缓存内容, 0表示不是。
耗时打点统计
我们一直关注页面加载的性能,问题到底出在哪儿,是终端耗时,网络耗时,还是页面逻辑耗时,我们在Weex页面上打了一系列点,Webview时期也打了很多点统计这些数据。终端、前端两份上报,目的是相互对账。
- 拦截请求开始、请求完成、RenderFinish计算网络耗时(请求结束 - 请求开始)、终端渲染耗时(RenderFinish - 请求结束)
- 终端通过js api提供前端创建instnace时间点
- 终端、前端两份上报,相互校验数据,生成性能报表,快速定位性能问题产生的阶段
终端接口调用耗时优化
1、Android 升级weex 0.16版本module接口调用耗时大幅减少。
终端接口耗时优化,这是一个比较现实的问题。最早接Weex时接的是0.12版本,当时调用一次module接口50-60毫秒,后来升级0.16,这个问题就解决了,Weex团队持续做优化,这是一件好事情。
2、减少启动页面时并发调用module接口。
现在接口单次调用2-3ms,但是并发请求性能很低,需要60ms以上,这个对前端页面是很大的损伤,希望这一块能够持续优化解决这个问题。
3、Android 调用时多次反射获取类名导致Dom渲染耗时过长。
Weex里的小bug,调用时多次反射获取类名,导致Dom渲染耗时过长。其实一次调用时间并不长1ms,需要调一百多次,就造成很大的耗时了。
Android Bitmap 内存优化
使用Fresco管线加载图片,加载后不持有图片引用。
问题:
- 会将用户当前不展示的图片也加载到内存中,实际占用内存变多,OOM问题严重。
- 引用不被持有会导致Bitmap被回收后仍然被使用(3.2 - 3.4版本top crash—SIGSEGV (SEGV_MAPERR))
优化: 仿照Fresco的DraweeController管理WXImageView的bitmap引用。 显示时加载,隐藏时解除引用等待回收。
OOM问题明显缓解,bitmap被回收的crash问题也得到解决。
GPU过度绘制优化
GPU过度绘制是终端开发很关注的问题,通过优化层级,提升渲染性能。我们上了Weex后没关注这个问题,重构按照以前的开发方式,设置很多层和背景色,生成的Weex页面打开Android调试发现大面积是红的,过度绘制很严重。
优化方法:
- 尽量不要设置背景色
- 不要过度嵌套,结构尽量扁平化
Weex实践分享——内部影响力
- 横竖屏切换方案支持
- 支持armabi-v7a的so包
- 组件统一开源:例如 jscore内核、debugger、gcanvas
- 更好的性能细分统计
- 高效的终端和Weex共享动态数据方案
- 更好的容错: iOS JS call Native时,参数传错会造成终端crash
原文发布时间为:2018-01-31
本文作者:渠宏伟
本文来自云栖社区合作伙伴“淘宝技术”,了解相关信息可以关注“淘宝技术”微信公众号