|
腾讯课堂小程序性能极致优化——综合篇-绍兴响应式网站建设制作开发为你呈现时间:2022-05-06 导语 | 如果你的小程序也遇到了性能问题,我们的实践经验也许可以给到你启发,我们从小程序的启动、加载到交互都进行了探索。 1. 缘起事情,要从一个周末惬意的下午开始说起…… 那天,手机突然被唤醒,弹出多条微信消息。原来是这周末正在校园推广的活动群发来的,想起之前大家有条不紊的开发进度,和产品沟通的友好过程,应该是活动反响不错。 现实是残酷的: “我们的小程序打开慢成狗!” “这个 loading 加载的过程也太久了!” “滚动加载有点卡,而且很容易报错……” 看到的是最直接的控诉。 看到用户的录屏,这几个问题确实是有出现,所以我们还是需要对小程序进行一次主流程的性能优化,三句控诉可以总结为3个点:
2.定位2.1. 启动慢收到反馈后第一反应是,用户是不是网速太慢了,自己跑一遍,发现自己的手机跑起来都是没问题的,灰常流畅,下意识的可能想录个屏回复过去。 不过有用户录屏在那,当然不能这么草率,所以我们查了下管理后台小程序在不同网络下的大盘数据:
于是我们从另外一个维度来看一下大盘数据:
从这里就可以看出问题来了,手机的性能对于小程序的启动速度影响非常大,低端机相对高端机有2-3倍的差距,特别是渲染层甚至有5-6倍的差距,而且问题反馈的用户所使用的手机也确实是中低端机,但用户使用什么手机我们也没法控制,那这里有办法去优化吗? 针对这个问题,我们需要了解一下小程序的启动过程,根据官方文档的介绍,小程序的启动可以分为下面几个步骤: 上图描述了用户点击小程序开始到页面开始请求数据的一个完整的冷启动过程,而小程序初始化的过程(信息准备、环境准备)占用了比较长的时间,但这部分的工作是由微信客户端来完成,开发者无法干预,所以我们只能聚焦于后续的步骤(下载代码包、注入代码包、初次渲染)。 根据官方文档的介绍,这一部分的可优化手段有:
后面4条在技术上没有特别好的限制手段,需要我们在 Code Review 的时候对复杂度和开销大的接口调用进行把关,复杂度这里还可以借助如 CodeCC(腾讯内部代码检查工具) 这类工具去进行分析,减少自定义组件数量,这个是比较难以抉择的,需要在代码可读性、可复用性之间做个取舍,不是本次优化的重点。 所以我们就重点关注的代码包体积问题,通过我们的 CI 记录可以收集到我们的总包大小: 可以看到主包体积达到1949.71KB,接近了2M的极限了,在通过依赖分析后,发现除了一些是未使用到的模块和组件外,很大一部分内容是静态资源,同时我们也在官方文档中看到这样一句话:
所以我们要减小代码包的体积,最直接的方法就是将非必要的资源去除掉:
同时我们还关注到,有一些分包特别小,但是由于是普通分包,在打开这些页面的时候还需要先下载主包,这里在包下载耗时上其实是有一些浪费的,比较典型的就是 WebView 页面,他们往往只需要对参数进行处理,对于主包的依赖不是很强,所以这里还有一个可以优化的点:
2.2. 请求慢我们通过日志查到这个用户的首页数据请求返回会到3-4s,请求慢在正常情况下会有这么两种情况:
我们通过日志统计发现用户的访问时间端,请求量跟平时保持一致,看大盘请求耗时的统计,也没有产生大的波动: 所以基本可以排除是后台的问题,虽然大盘的数据在500ms左右,但是当用户网络不好的情况下,这一块要怎么保证呢? 答案当然是做提前拉取,当用户冷启动的时候,我们可以使用小程序官方提供的数据预拉取能力提前拉取,从小程序的启动耗时看,完全可以 cover 掉我们的接口请求耗时,可以让小程序启动成功后就直接渲染页面。 在热启动的情况下,请求慢主要体现在用户交互时发生的请求和页面切换时发生的请求,交互的情况我们下一节在分析,这里主要看页面切换,从我们的统计数据来看,页面切换的耗时大概在400ms左右,而其中能够利用的时间大概是50ms-100ms: 利用页面切换的这个时间提前对页面的数据进行加载,可以减少用户感观上的数据请求时间,同时在第一次请求之后可以根据一定的策略对页面的数据进行缓存,从而可以达到二次进入页面秒开的效果。 总结来看,请求慢的优化手段有下面几个,而且理论上效果都会很显著:
2.3. 交互慢先说一下这里的交互慢具体指什么,我们收到用户反馈的现象是:用户首屏顺利加载出来之后,后续滚动加载和一些按钮点击的响应非常慢并且很容易报错。收到这个反馈后定位了很久,讲道理如果是因为用户网络问题导致的请求很慢,应该所有的请求都会很慢,但是用户表现出来的现象是后续的加载以及交互很慢,反而首屏还算正常。 通过日志查询,我们发现这个用户的请求报错都是请求超时,为什么超时会集中在交互加载这里呢?定位了一段时间后我们发现一个用户的报错都集中在首屏加载之后就立马下滑或者点击,如果过了一段时间再点击又不会报错。 发现这个现象后,我们想到了官方文档关于网络使用说明的一个限制:
再结合我们对于 wx.request 的封装,请求超时的计时器是从调用 wx.request 的时候就开始了,如果请求并发超过了限制,那么就很容易出现请求超时,而当我们从第一个业务接口请求到数据后就会进行一系列的数据上报,包括 pv、组件曝光、关键链路打点等等,所以我们利用 Whistle 的 resDelay 方法,将我们的上报请求都延迟5000ms返回,果然就复现了用户反馈的那种情况。 找到问题之后也就明确了需要优化的方向:
交互慢还有别的原因吗?在继续挖掘性能瓶颈的过程中,发现腾讯课堂小程序的课程详情页内容非常多,有5-6屏的高度,而用户只会关心首屏是不是更快的呈现出来,但是我们原本的处理方式是比较粗暴的,拿到详情页的数据之后对数据进行处理,格式化成整个页面所需要的数据之后一次性调用
2.4. 优化点归总再归总一下需要优化的点和方向:
3. 优化3.1. 启动优化3.1.1. 独立分包由于用户反馈主要是因为校园推广活动来的,而活动页面我们是通过 WebView 内嵌 H5 来承载的,而 WebView 页面的启动过程和小程序原生页面还不太一样: 实际上 WebView 页面只需要完善登录态传递的功能,对于主包的依赖不是很大,而且这部分页面更大的性能问题需要在 h5 那边来优化,所以我们第一时间对其进行了独立分包处理。 最终的优化效果还不错,因为启动过程中不需要下载主包,启动性能提升了30%。 3.1.2. 静态资源上CDN我们小程序构成主要是由原生页面 + kbone 页面组成的,kbone 是采用的官方的方案,通过 webpack 构建,有很多单独打包静态资源的方案。而我们的原生页面是使用 gulp 进行构建的,原来主要的功能是将源码中的 ts 转成 js,同时对 css 文件通过 postcss 转成 wxss,由于 wxss 不支持引用相对路径,所以在 wxss 中引用的图片和字体都是转成 base64 的,然后对其余的文件如 json、wxml 文件则直接复制到产物中去。 这样的处理方式比较粗暴,通过 postcss 将 background-image 所引用的本地图片都转成 base64,还会导致很多图片在项目中占用了2倍的体积。 所以我们首先需要将源码下的静态资源匹配到并单独构建出来,并且为了规避同名文件的问题,需要对资源打个 hashtag,我们这里需要用到一个 gulp 插件 将图片上到 CDN 后,把 css、js、json、wxml 中的引用路径替换成 CDN 地址,具体的替换逻辑如下。 3.1.3. 过滤未使用组件随着业务的迭代,不可避免的会有一些组件被废弃了但是难以察觉,通过我们团队开发的小程序脚手架 imweb-miniprogram-cli 对页面所使用到的组件进行分析,可以把项目中未使用到的组件给过滤出来,不会打包到最终的产物中,大致的思路如下: 从 可以看到在我们的项目中就发现了好几个未使用到的组件。 3.2. 请求优化3.2.1. 数据预拉取数据预拉取需要在小程序的管理后台开启,数据来源可以选择开发者服务器或者云开发,选择开发者服务器的话会有一些限制,如果是直接填写 CGI 地址,就只能拉取一种数据,不够灵活,而如果再搭建一个服务去做预拉取涉及到的工作量又会很大,所以我们选择的是云开发的方式,大致流程如下图: 当小程序启动的时候,微信客户端会根据配置去拉取指定的云函数,在云函数中通过 cl5 调用业务后台的服务拉取到需要的数据,拉取到后客户端会将数据缓存在本地,当小程序启动成功后,在业务代码中调用 在云函数中可以拿到本次小程序启动的
不过要注意的是,因为小程序自身做了很多初始化的优化,有可能在小程序启动后,预拉取的数据还没有返回,所以我们做了进一步的优化,在业务拉取的过程中通过 3.2.2. 提前拉取 & 数据缓存前面已经提到过,提前拉取就是要利用小程序切换页面的空隙开始拉取数据,从而在感官上较少数据请求的时间,整体的逻辑是通过封装的跳转逻辑,对应的页面添加不同的数据拉取逻辑,并将拉取的 promise 挂载在 App 上,当页面切换完成后优先使用 App 上的 promise 来获取数据。 数据缓存则是在数据拉取成功后,将比较固定的数据通过 提前拉取 3.3. 交互优化3.3.1. 业务请求保障保障业务请求的核心思路是让业务请求优先,我们封装了一个 排队请求模块 ,通过对 3.3.2. 分步渲染这里的方案相信大家也能很好理解,主要是优先处理首屏需要的数据并通过
而小程序代码执行顺序也遵循JS的事件循环机制,仅仅是在处理后的数据调用 4. 成果 经过这一系列的优化,效果还是比较明显的: 4.1. 包大小在包大小方面:
从启动耗时的数据看,下载耗时和JS注入耗时都有明显的下降: 再看打开耗时分布,可以看到3s内打开的用户比例有明显增加,从56.26%增加到64.25%; 4.2. 请求耗时数据预拉取,提前拉取,数据缓存在冷启动和页面切换时都起到了很不错的效果: 首页请求速度从平均400ms下降到50ms,优化了87.5%; 课详页的请求速度从平均800ms下降到90ms,优化了88.75%; 而数据缓存让二次访问时页面可以秒开: 使用排队请求之后,对网络请求顺序的干预效果还比较明显,灰度用户业务请求耗时平均有50-100ms,约15%的优化; 同时我们通过分析耗时分别在80分位、50分位、20分位的效果发现,请求耗时越长,优化效果越明显,也就是说在弱网情况下能够更好的发挥作用。 4.3. 渲染使用分步渲染后,我们的页面可以在处理完首屏的基础数据之后就立即开始渲染了,由于我们的目录结构比较复杂,处理起来耗时比较长,所以第二部才处理目录,实际的渲染效果如下图: 首屏可以比原来提前100ms-150ms渲染出来。 5. 总结我们本次的性能优化对小程序启动、请求、交互、渲染多个方面都进行了性能的挖掘,是在对基础库版本要求不高的情况下能做到的极致了。 以我们的核心页面首页和课程详情页来说:
还有更多的优化手段吗?官方还提供了一些比较高级的功能,对基础库版本要求比较高的,例如:
利用这些能力可以在更多细节上进行优化,我们也将进一步探索和跟进,如果你有更好的方案欢迎来到微信开放社区 和我们一起讨论。 |