为何小程序上线了,他们的内心却留下遗憾?
前言
出于多端投放和开放生态的考虑,闲鱼开始接入整个阿里小程序体系。闲鱼在9月份迅速上线了第一个小程序鱼塘小程序,由于刚接触不熟悉小程序体系,整体性能上有比较大的优化空间,主要表现在以下问题: 小程序加载慢,低端 Android 机(Android vivo Y67)上首屏时间接近6s 滚动卡顿,在 iPhone 7P 上滚动帧率平均在 40fps 左右 * 滚动多屏数据之后 Tab 点击切换慢,在 iPhone 7P 上切换 Tab 等待时间 3-5 秒,瞬时帧率低于 30fps 小程序由于其逻辑和渲染分离的架构特点,除传统 H5 优化手段之外还有其他不同点。本篇文章主要分析小程序构架对渲染的影响,以及鱼塘小程序下具体优化手段。
小程序架构
在分析具体优化 Case 之前我们先看下小程序架构,先要了解架构才能清楚如何去优化具体的业务代码。阿里小程序采用支付宝小程序的架构,这里引用一张支付宝小程序页面生命周期图。
目前市场上所有小程序都采用逻辑(worker)和渲染(webview)分离的方式。这样带来的好处是:
能够满足对于外部应用管控的诉求,由于业务逻辑没有运行在 webview 上,所以无法通过浏览器的API直接操作渲染动作,意味着不能通过脚本做一些动态化操作。所有渲染相关操作都是通过 axml 来定义,外部应用进行更改都需要通过平台审核。
多个页面可以共享同一个 JS 运行环境,整个小程序生命周期可以共享全局应用上下文,接近 App 的开发体验。
页面渲染与业务逻辑分开执行,不会出现 JS 逻辑执行导致渲染卡住的情况,有利于提升整体渲染性能。
但是这样也会造成一个显而易见的问题,页面性能强依赖 webview 和 worker 的通信效率: 1. webview 和 worker 之间的通信是异步的。这意味着当我们调用 setData 时,数据变更不会立即体现到页面渲染上,而是需要从 worker 异步传输到 webview 。 2. 数据传输时需要序列化为字符串,然后通过 evaluateJavascript 方式传输,数据大小会影响传输性能。
小程序逻辑和渲染分离的架构造成它与传统 H5 性能优化方式上的一些差异。小程序性能优化可以参考看下官方推荐的一些性能优化建议,简单来讲需要特别注意 setData 操作的频次和传输数据,接下来我们结合鱼塘小程序具体案例一块探讨下。
业务层优化
鱼塘小程序是一个类似兴趣圈子下的内容聚合场景,用户在这里可以无限加载浏览内容,还会点击 Tab 切换浏览不同维度的内容。我们需要重点考虑小程序加载流畅度、滚动顺滑度以及 Tab 点击切换时候界面响应速度。之前版本的鱼塘小程序在低端 Android 机(Android vivo Y67)上首屏时间接近6s,在 iPhone 7P 上滚动帧率平均在 34fps - 60fps,点击 Tab 切换瞬时帧率在 30fps 左右,下面针对几个具体 Case 讨论解决方案。
加载慢
『BEFORE』
『AFTER』
问题表现
从打开小程序到最终渲染出来经历了短暂的白屏
原因分析
加载整体耗时包括小程序容器初始化、数据请求以及请求结果返回到渲染,需要针对每个时期做优化
优化手段
引入小程序实例保活机制,降低小程序启动耗时
将数据请求提前至 page.onLoad 中,请求阶段加入闲鱼 Loading 提示,通过交互减少用户焦虑感
首屏数据离线化,在首屏数据到达前预渲染,在容器测请求提前至与小程序实例启动并行或更前
将首屏数据进行拆分,初始化只 setData 可见视图对应的数据
滚动卡顿
『BEFORE』
『AFTER』
问题表现
页面滚动掉帧感明显,粘手度低
原因分析
由于需要监听页面滚动距离触发顶部 Bar 显示,Page 层监听了 onScroll 事件,并在回调中频繁的调用 setData
加载下一页 Feeds 的请求回调中,解析数据时多次调用 setData
Feeds 卡片内部监听了组件的 onAppear 和 onDisappear 事件,并在回调中调用 setData,目的是为了不重复发送曝光埋点
优化手段
针对小程序 webview 和 worker 通信的机制,我们需要减少 setData 的调用频率与传输数据大小。 优化了 onScroll 回调逻辑,改为只有在部分时机(滚动距离在一定范围内)下才会触发 setData,且只做局部渲染 加载下一页 Feeds 的请求回调优化了数据解析逻辑,只调用一次 setData,并参考官方优化建议使用 $spliceData 渲染长列表 将 setData 的数据大小进行优化,只传输会影响视图的相关数据 不再监听组件 onAppear 和 onDisappear 事件,改为监听组件的 onFirstAppear 事件,只有第一次 Appear 的时候才执行曝光操作
Tab 切换响应慢
『BEFORE』
『AFTER』
问题表现
加载几页 Feeds 后,切换 Tab 开始出现明显卡顿,需等待 3-5 秒,部分 Android 机器上更为严重,偶尔会 Crash
原因分析
Tab 切换时在短时间内做了太多事情:切换 Tab Current 状态、销毁 Feeds 列表、展示 Loading 动画、发起数据请求 -> 渲染新列表,这样高并发大面积的内容更新导致小程序视图层数据消费阻塞,从而产生卡顿感。
优化手段
将 Tab 切换时的任务拆解开,分四个阶段进行:
切换 Tab Current 状态,执行 Tab 切换动画
在 Tab 切换动画完成后将页面滚动到 Tab 刚好 Sticky 住的位置
销毁 Feeds 列表,展示 Loading 动画
发起数据请求 -> 渲染
经过这样的拆解,将之前的『高并发大面积』转换成了『分阶段可控制』的更新方式,随之带来的就是界面上的流畅
容器层优化
小程序容器通过离线缓存、数据预加载、小程序保活等机制来优化整体性能。然而在富交互场景中,webview 上的控件渲染会遇到很多性能瓶颈,目前阿里小程序支持在 webview 中内嵌 native 组件来提升整体性能,鱼塘小程序中有大量视频内容场景,使用的 video 组件就是 native 原生组件。这类组件脱离 webview 线程之外渲染,但是由于是覆盖在 webview 之上,所以在 webview 内无论怎样修改 z-index 都无法将元素覆盖在原生组件之上。
为了解决这个问题,小程序框架同学又设计了 cover-view ,它可以覆盖在 native 组件之上,比如视频上方的播放按钮就可以用 cover-view 盖上去。最终线上鱼塘小程序通过同层渲染 video 组件之后用户侧体验有比较大的提升。
总结与展望
经过优化之后,目前线上鱼塘小程序相比之前版本有显著提升:
针对这个业务场景下的小程序依然有很多可以继续优化空间,例如我们将每个鱼塘实例化独自小程序,这样可以针对每个鱼塘小程序去进行保活等。此外在小程序性能优化相关上,我们认为可以在研发阶段提供一个包含性能告警的容器,通过监听 setData 的调用频率与传输数据大小,对开发者一些可能会影响性能的代码写法进行提示。未来我们会持续在闲鱼小程序生态建设上发力,整合集团研发资源建立闲鱼小程序研发全链路最佳实践,提供外部服务商入驻开发三方小程序的良好体验。
扫一扫,关注我们