文 | vic yao
小程序科普类的文章已经很多了,这里讲下针对小程序的优化方法,可以有效提高小程序的响应速度和用户体验。当然,开发体验也提高不少。
1. 提高页面加载速度——前端永恒不变的话题
在小程序这个环境下,怎样提高页面加载速度呢?
这个问题很大,我把问题再改得具体一点:如何缩短从用户点击某个链接,到打开新页面的这段时间?
这里抛一个核心关键点:
从页面响应用户点击行为开始跳转,到新页面 onload
事件触发之间,存在一个延迟,这个延迟大概在 100-300 ms 之间(不同机型会有差异)。
我们可以利用这段延迟,预先发起新页面所需要的网络请求。这样一来,就节省了 100-300 ms(或者一个网络请求的时间)。
那么,如何利用这段 gap,提高页面加载速度呢?说白了,就是实现跨页预加载数据的功能。但而这种跨页面的调用,很容易把逻辑搞得非常复杂。
所以,我们的目标就是:将预加载的逻辑隐藏于无形中,不增加任何的页面间耦合,以及开发复杂度。
下面以腾讯视频小程序为例,讲解下技术实现。
小程序首页:
当用户点击海报图后,会执行以下代码:
接下来程序会加载播放页:
播放页主要代码:
可以看到,不管是外部页面的调用还是实际逻辑的实现都非常简洁。
在第二个页面中,我们扩展了 Page
的生命周期函数,增加了 onNavigate
方法。该方法会在页面即将被创建、但还没开始创建的时候执行。
老司机也许会发现这里有点蹊跷。在首页点击的时候,播放页根本就没有创建。对象都不存在,怎么访问到里面的方法呢?
这里就要说下微信的页面机制。
在小程序启动时,会把所有调用 Page()
方法的 object 存在一个队列里。每次页面访问的时候,微信会重新创建一个新的对象实例(实际上就是深拷贝)。
也就是说,在前一页面在执行点击响应事件的时候,新页面的实例还没创建,这时候调用的 onNavigate
方法,实际上是 Page
对象的原型(小程序启动时候创建的那个)。
而接下来马上要被创建的新页面,又是另外一个 object。所以,在 onNavigate
和 onLoad
方法中,this
指针指的不是同一个对象,不能把临时数据存储在当前 object 身上。
因此,我们封装了一对全局的缓存方法:$put()
和 $take()
。
为了通用性,Page
上用到的公共的方法,比如 $route
、$put
、$take
等,都定义在了 Page
的基类里面。
基类还同时保存了所有页面的 list,这样就可以做到根据页面名调用具体页面的 onNavigate
方法。
当然,并不是每个页面都需要实现 onNavigate
方法,对于没有定义 onNavigate
方法的,$route
函数会跳过预加载环节,直接跳转页面。
所以对于开发者来说,不需要关心别的页面实现了什么。
2. 用户行为预测
在上面的例子中,我们实现了用户主动点击页面,提前加载下一页面数据的方法。
而在某些用户的行为可以预测的场景下,我们可以在用户还没点击的时候,就预加载下个页面的数据。
这样就可以让下个页面秒开,进一步提升体验的流畅性。
继续以腾讯视频小程序为例,主界面分为 3 个页卡(大部分小程序都会这么设计)。通过简单的数据分析,发现进入首页的用户有 50% 会访问第二个页卡。
所以,预加载第二个页卡的数据,可以很大程度提高用户下个点击页面的打开速度。
同样,先看看代码实现。首页预加载频道页的姿势:
频道页的实现方法:
跟第一个例子类似,这里定义了一个 $preLoad()
方法,同时给 Page
扩展了一个 onPreload
事件。
页面调用 $preLoad()
后,基类会自动找到该页面对应的 onPreload
函数,通知页面执行预加载操作。
跟第一个例子不同,这里预加载的数据会保存在 storage 内。
因为用户不一定会马上访问页面,而把数据存在全局变量会增加小程序占用的内存。微信会毫不犹豫的把内存占用过大的小程序给杀掉。
也许对于大部分有 app 开发经验的同学来说,更普遍的做法是先让页面展示上次缓存的数据,再实时拉取新数据,然后刷新页面。
这个方法在小程序上,体验并会不太好。原因是小程序的性能以及页面渲染速度都不如原生 app。
将一个大的 data
传输给视图层,是一个很重的操作。因此不建议采用这种方法。
3. 减少默认 data
的大小
刚刚说到,页面打开一个新页面时微信会深拷贝一个 Page
对象。
因此,应该尽量减少默认 data
的大小,以及减少对象内的自定义属性。
有图有真相:
以一个带有 100 种属性的 data
对象为测试用例,在 iPhone 6 上,页面的创建时间会因此增加 150ms。
4. 组件化方案
微信没有提供小程序的组件化方案(相信一定在实现中)。但开谈不说组件化,写再多代码也枉然。这里演示一个简单的组件化实现。
以腾讯视频播放页为例,页面定义如下:
其中,P()
函数是自定义的基类。这是一个非常有用的东西,可以把所有通用的逻辑都写在基类里面,包括 PV 统计,来源统计,扩展生命周期函数,实现组件化等。
函数第一个参数是页面名称,作为页面的 key。第二个是 page
对象,其中扩展了一个 comps
数组,里面就是所有要加载的组件。
以播放器组件 /comps/player/index.js
为例:
组件的定义跟一个普通 Page
对象一模一样,有 data
属性,以及 onLoad
、onShow
等事件,也有页面响应的回调方法。WXML 模板里定义的事件和 JS 事件一一对应。
基类做的事情,就是把这些组件对象的属性和方法复制到 Page
对象上(浅拷贝)。
其中 data
属性会 merge 到一起。而微信预定义的生命周期函数(包括自己扩展的),则封装成队列按序执行。
比如,当系统调用 onLoad
方法时,实际上是执行了所有组件的 onLoad
方法,最后再执行 Page
的 onLoad
。
以上说的,都是逻辑层部分。至于 WXML 模板和 WXSS 部分,就要手工 import 过去了。
WXML:
WXSS:
5. 其他
虽然小程序已经足够小巧,但启动速度还是有那么 2-3 秒,无法做到秒开。我尝试对小程序的启动时间做优化,但没有找到多少有价值的优化点。
也许,这里面大部分时间,都消耗在了微信跟服务器通信的过程中。还是期待微信可以不断迭代优化。
原文地址:https://github.com/ysbcc/miniapp
往期精选文章
本文由知晓程序授权转载,关注微信号 zxcx0101,可获得以下内容和服务:
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有