Home > Article > WeChat Applet > Practice summary of small program performance optimization
Let’s briefly introduce the project, which is a relatively conventional ordering app.
The interface is as shown:
The left is the category menu, and the right is the long list. There are multiple categories of products. You can continue to scroll after scrolling through a single category. Switch to the next category, and the selected status of the category menu on the left will switch to the category displayed in the current product list.
Considering a better user experience and referring to Meituan and other ordering applets, the data of this product list is returned in one go. The problem currently encountered is that when the number of products is large, the first rendering time is very long, and the page will freeze.
Xiaosheng bb: In fact, the original code (due to historical reasons) was too poorly written...OTL
Put a picture first
Xiaosheng bb: I can’t even stand the mini program anymore, so I need to warn you
WeChat developer tools have warned you, And the prompt also contains the location of the specific code, so the key is this setData
! ! !
We can first take a look at some official suggestions for mini program performance and setData
optimization. (developers.weixin.qq.com/miniprogram…)
Specific practice:
setData
You cannot transmit too much data at one time. If the list is too long, you can Render separately [such as converting to a two-dimensional array and rendering one array each time in the loop]. v1: Simple and rough version
// 每次渲染一个分类// 假设goodsList是一个二维数组goodsList.forEach((item, index) => { this.setData({ [`goodsList[${index}]`]: item }) })复制代码
There is a problem when writing like the above. The first screen rendering of the page is fast, but when you click on the page operation (such as the purchase button, etc.), the page will freeze. Wait and wait for a response. The operation feedback is seriously delayed.
In fact, this is because this cycle reduces the number of single setData
, but turns it into a multiple cycle of setData
. We look at the first screen display Okay, but in fact, other categories (other arrays) are still rendering, and the thread is still busy. The JS thread has been compiling and executing rendering. The click event cannot be passed to the logic layer in time, and the logic layer cannot pass the operation processing results to the view in time. layer.
v2: Timer hack version
Since the js thread is busy rendering, we can force it to stop first. So there is a timer hack version of v2.
// 每次渲染一个分类let len = data.goodsList ? data.goodsList.length : 0;let idx = 0let timer = setInterval(() => { if (idx < len) { that.setData({ [`goodsList[${idx}]`]: data.goodsList[idx] }); idx++ } else { clearInterval(timer) } }, 15);复制代码
Now the first screen rendering speed problem has been solved, and the delayed response problem of clicking buttons has also been solved. It’s just that the code is a bit hacky, which makes me suffer from obsessive-compulsive disorder
v3: Big Killer - Virtual List
The simple principle of virtual list is to only render the current display screen area and the front n screens and back n screens Use a separate field to save the current array that needs to be displayed (that is, n screens before the current screen and n screens after the current one). Each time the list scrolls, the data that needs to be displayed is recalculated. If this field is updated, the page will be updated accordingly. . This ensures that the number of element nodes on the page is not too large and can support long list requirements for large amounts of data.
For more detailed principles and implementation, students can search for it themselves and will not be expanded upon here.
The mini program also has an official open source virtual list component: recycle-view
setData
can support granular updates and specify specific attributes. For example, if you need to update the small number in the upper right corner of the product for operations such as additional purchases, you can write it like this:
this.setData({ [`goodsList[${categoryIndex}][${goodsIndex}].num`]: goodsItem.num })复制代码
data
. Do not update with setData
because setData
will trigger page rendering. eg:
Page({ data: { ... }, // 跟页面渲染无关的数据 state: { hasLogin: false, }, ... })// 更新的时候直接赋值就行this.state.hasLogin = true复制代码
PS: Or you don’t even need to mount it to the page
object, just save it directly with an ordinary variable.
If the image size in the long list is not limited, a large number of large images will occupy a lot of memory, which may cause the iOS client memory usage to increase, thus triggering the system Recycle mini program page. In addition to memory issues, large images can also cause page switching lags.
The solution is to take a picture with just the right size (2x-3x the picture) based on the size of the currently displayed picture area.
It is recommended to use a CDN for images. Generally, CDN service providers that provide image services will provide an interface for cropping images, and then the interface only returns the original image link, and the front end passes parameters to crop the image as needed. The specific front-end approach can be to write a public image processing method, or encapsulate the image component yourself.
Attached is the image cropping API document of commonly used image CDN service providers:
比如在该点餐页面进入时需要获取定位,然后根据定位获取最近的门店,前面两个接口都需要请求(具体可以根据业务需求),而最后如果获取到的距离最近的门店跟上次一样,则不需要重新获取店铺详情和商品数据。
还是该点餐页面流程,像上文说过的,进入页面时需要获取定位接口,等定位接口返回结果了再拿定位取值去获取距离最近的店铺,最后才是请求店铺和商品数据。
这三个接口是串行的。此时如果我们每个接口都弹出一个loading提示,就会出现loading显示一会儿,消失,又显示一会儿,又消失……这样的现象,这样的体验是不太好的。
建议可以通过封装请求,并且在请求里统一处理loading,来合并短时间内多次发起请求的多个loading。
eg:
let showLoadingTimer = null;let showRequestLoading = false; // 标记是否正在显示loading/** * 封装request * @param {*} {showLoading:是否需要显示loading, options:request参数,如url,data等} */function request({showLoading = true, ...options}) { // 显示request loading handleShowLoading(showLoading) wx.request({ ... complete() { // 关闭request loading handleShowLoading(false) } }) }/** * 封装request loading * 短时间内如果调用多次showLoading,会合并在一起显示,而不是每个都闪现一下 * @param showLoading */function handleShowLoading(showLoading) { if (showLoading) { // 显示loading clearTimeout(showLoadingTimer); if (!showRequestLoading) { showRequestLoading = true; wx.showNavigationBarLoading(); wx.showLoading({ title: "加载中", mask: true }) } } else { // 200ms后关闭loading showLoadingTimer = setTimeout(() => { showRequestLoading = false; wx.hideNavigationBarLoading(); wx.hideLoading() }, 200) } }复制代码
比如这个点餐页每次 onShow
都会调用定位接口和获取最近门店接口,但是不显示loading,用户就没有感知,体验比较好。
需要关注接口的粒度控制。 因为有时候合并接口,前端可以减少一次请求,体验更好;但有时候如果接口的数据太多,响应太慢,就可以考虑是否某部分数据可以后置获取,让主要的页面内容先渲染出来,根据这个设计来拆分接口。
比如项目中的点餐页面,原来购物车数据和商品规格弹窗显示的详情数据都是在获取店铺商品接口一次性返回的,而这个接口本来由于设计需要一次返回所有商品,就会造成数据量太大,而且后端需要查询的表也更多。于是把获取购物车,和商品详情接口都拆分为单独的接口,获取店铺商品接口的响应时间就减少了,页面也能更快显示出来。
其实上面提到的逻辑优化和接口优化很多都是细节,并不是太高深的技术,我们平时迭代的时候就可以注意。而体验方面的优化则需要前端同学在前端技术以外更多关注用户体验和设计方面的知识啦,而且这也是一个有追求的前端应该具备的技能……←_←
所以嘛……技术路漫漫,大家共勉吧
相关免费学习推荐:微信小程序开发教程
The above is the detailed content of Practice summary of small program performance optimization. For more information, please follow other related articles on the PHP Chinese website!