作者:haoyechen 陈浩烨
编辑:mavisxlmeng 蒙雪莲
在微信小程序的广告投放过程中,用户的体验对广告的转化有着很大的影响。随着功能的增加,小程序包逐渐增大并影响体验。而小程序独立分包较小并且可以独立加载运行,利用该特性,我们来探讨下开发广告包的优化思路。
1
背景
与公众号H5相比,小程序有更接近原生App的体验优势。由于微信小程序采用的是类离线包加载方案,当用户第一次打开时会先下载好所有代码,然后再加载页面,获得跟app类似的本地化体验;当用户再次进入时,会直接使用已下载的代码包,省去了下载的过程,从而优化打开速度。
这种设计方式在广告投放时带来了一个显著问题,第一次打开小程序时间较长。从实际广告投放效果来看,小程序包的大小与小程序广告落地页到达率(用户从点击广告到加载完页面的比例)呈现明显的负相关。新用户固然要下载很大的代码量,导致白屏时间十分长。老用户在代码有版本更新时也需要重新下载。最坏的情况下,部分投放的到达率不足40% ,这显然是无法接受的。
2
用户忍耐度
用户对响应时间的容忍度,性能测试中有个通用的2-5-10原则。
当用户能够在2秒以内得到响应时,会感觉系统的响应很快;当用户在2-5秒之间得到响应时,会感觉系统的响应速度还可以;当用户在5-10秒以内得到响应时,会感觉系统的响应速度很慢,但还可以接受;而当用户在超过10秒后仍然无法得到响应时,会感觉糟透。

3
独立分包
处理这个问题之前,我们来看一下小程序首次加载的流程:

可以看出,由于首次加载包含下载过程,复杂业务的小程序主包一般十分大,再加上投放包一般不放在主包,那么下载的小程序包大小可能会高达2~4m,再到页面登录和渲染,显然不能满足我们的投放的要求,所以我们考虑采用独立分包来完成投放。
独立分包可以独立于主包和其他分包运行,从页面进入小程序时,不需要下载主包,可以减少小程序包大小。
我们采用一个投放包在虚拟4g环境测试,(下载速率约在1.5MB/s),对比主包和采用了独立分包的加载时长(备注:主包加子包大小为2841KB,进入主包还包括一个过场动画页面;独立分包大小为655KB),加载时间是多次测试的平均值。

可见,即使在较好的网络环境,加载时间依然差距很大,高达3秒的加载时间差距。下面是该投放包采用了独立分包后的到达率对比:

由于小程序主包过大,所以这个优化效果还是很明显的,采用独立分包后的到达率提升了近一倍。
4
独立分包的问题与解决
引入独立分包以后,我们又发现新的一系列问题:
跳转加载主包时间过长的问题 小程序投放总是不可避免要给小程序主包引流。当投放包需要跳转其他普通子包页面时,小程序会先下载主包,再下载对应的子包。当处于下载阶段时,页面会维持较长时间的白屏页面。
对此,可以利用小程序的分包预下载能力,可以配置进入某个页面时预下载可能会用到的分包,避免在页面切换时在白屏停留过久。
配置方法:
预加载分包行为在进入某个页面的时候触发,在通过app.json 增加 preloadRule。
"preloadRule": {"pages/index": {"network": "all","packages": ["important"]},"sub1/index": {"packages": ["hello", "sub3"]},"sub3/index": {"packages": ["path/to"]},"indep/index": {"packages": ["APP"]}}
2.数据共享与登录态 原先小程序在独立分包与普通包之间无法共享登录态,这会导致用户从独立分包进入普通包其他页面时要重新登录,这个体验也不合理。
在小程序旧版本基础库sdk里,独立分包使用getApp()无法获取app实例,无法实现独立分包和小程序其他部分的全局变量共享。为了在独立分包中满足这一需求,基础库 2.2.4 版本开始 getApp支持 allowDefault参数,在 App 未定义时返回一个默认实现。这就使得我们能够打通主包和独立分包的登录态,保证用户在之间的交互衔接。
利用共享数据,我们通过下图的流程,实现了独立分包在共享主包登录态的同时,也能够独立进行登录。

而且主包通过getApp拿到登录态,可以减少页面请求时间。
对于页面间的公用数据,通常是将数据挂在App对象上。但引入了独立分包后,判断的逻辑就变得复杂了。
由于主包加载,App被注册时,默认实现中定义的属性会被覆盖合并到真正的App中,这就使得我们可以在独立分包处理一些公用逻辑,减少返回主包的成本。
为了不影响主包的通用对象,可以使用一个临时对象加载主包全局数据。
const SUBPACK_KEY = 'AppKey';let subApp;export function getSubApp() {let app = getApp({ allowDefault: true }) || {};return subApp = subApp || app[SUBPACK_KEY] || {};}export function setSubApp(subApp) {let app = getApp({ allowDefault: true }) || {};return app[SUBPACK_KEY] = subApp || {};}
同时利用单例模式保证通用事件和对象的唯一性,这样就可以兼容处理主包和分包的数据范例:
function globalData(fn) {// 判断是否存在实例if (typeof globalData.instance === 'object') {return globalData.instance;}//初始化内容this.fn = fn;//缓存globalData.instance = this;}
3.发布灵活性
由于广告投放成本较重,为了优化投放效果、降低成本,需要频繁地优化版本。为了发布灵活性,我们将部分页面 h5 化,因此小程序跳H5时需要通过链接url传输参数。但是在某些复杂场景,会出现链接传输数据过多问题。
小程序和 h5 的通信可以通过 JSSDK 和 postMessage 通道来实现。
// H5页面引入JSSDK可向小程序进行通信wx.miniProgram.navigateTo({url: '/path/to/page'})wx.miniProgram.postMessage({ data: 'foo' })wx.miniProgram.postMessage({ data: {foo: 'bar'} })
webview组件可以通过bindmessage事件获取数据 H5页面向小程序 postMessage 时,会在特定时机(小程序后退、组件销毁、分享)触发并收到消息
小程序则需要通过链接携带参数传给H5页面,当然链接数据不能过多。也可通过映射对象解决,通过同步api请求来维护这个映射,这能解决数据过多的问题。
这样结合起来可以充分保证H5页面与小程序页面的正常业务数据交互。
5
总结:
小程序独立分包适合广告投放场景,而提高转化则是投放的核心。用户转化和小程序分包性能息息相关。通过采用小程序独立分包,以及一系列的优化机制,我们的投放到达率逐步升级到75%+,这就使得我们的投放业务更加的高效。
对于产品,我们应该始终追求更加极致的性能和效率,这样才能推动业务的最优化发展。不同的业务会面临不同的用户场景,善于从遇到的问题为出发点,基于底层原理,探究产品及用户体验提升的各种可能性,也是每位开发者必备的能力,希望本文能给予一点启发。






