实践总结小程序性能优化

coldplay.xixi

coldplay.xixi

2020-11-02

3170人浏览

转载

微信小程序开发教程栏目总结小程序的性能优化。

项目简述 & 问题

先简单介绍一下项目,就是一个比较常规的点餐小程序。

界面如图:

左边是分类菜单,右边是长列表,有多个分类的商品,单个分类滚动完后可以继续滚动切换到下一个分类,同时左边的分类菜单选中态会跟着切换到当前商品列表显示的分类。

考虑到更好的用户体验,以及参考了美团等点餐小程序,这个商品列表的数据是一次性返回的。目前遇到的问题就是,当商品数量比较多时,首次渲染时间很长,而且页面会卡顿。

优化方向

逻辑优化

小声bb:其实就是原来代码(由于历史原因)写得太烂了……OTL

先放个图

小声bb:连小程序都看不下去了,要警告了

微信开发者工具都有警告了,而且提示里面也有定位到具体代码的位置,所以关键就是这个 setData !!!

我们可以先看看官方对于小程序性能以及 setData 优化的一些建议。(developers.weixin.qq.com/miniprogram…)

具体实践:

1. setData 不能一次性传太多数据,如果列表太长,可以分开渲染【比如转化为二维数组,每次循环渲染一个数组】。

v1:简单粗暴版

// 每次渲染一个分类// 假设goodsList是一个二维数组goodsList.forEach((item, index) => {    this.setData({
        [`goodsList[${index}]`]: item
    })
})复制代码

像上面这样写会有一个问题,页面首屏渲染是快了,但是点击页面操作(比如加购按钮等),页面会卡住,等一下才有反应,操作反馈延迟严重。

其实这是因为,这个循环是把单次 setData 数量减少了,但是却变成了循环多次 setData,我们看着首屏显示好了,但是其实其他分类(其他数组)还在渲染,线程还是忙碌状态,JS 线程一直在编译执行渲染,点击事件不能及时传递到逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层。

v2:定时器hack版

既然js线程忙着渲染,那我们可以强制让它先停下来。于是有了v2的定时器hack版。

// 每次渲染一个分类let len = data.goodsList ? data.goodsList.length : 0;let idx = 0let timer = setInterval(() => {    if (idx <p>现在首屏渲染速度问题解决了,点击按钮延迟响应问题也解决了。就是代码有点hack,逼死强迫症</p><p>v3:大杀器——虚拟列表</p><div class="aritcle_card flexRow artxards">
											<div class="artcardd flexRow">
												<a class="aritcle_card_img" rel="nofollow" href="/ai/984" title="Frase"><img
														src="https://img.php.cn/upload/ai_manual/000/000/000/175680021196501.png" alt="Frase" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
												<div class="aritcle_card_info flexColumn">
													<a rel="nofollow" href="/ai/984" title="Frase" class="overflowclass">Frase</a>
													<p class="overflowclass">Frase是一款出色的长篇 AI 写作工具,快速创建seo优化的内容。</p>
												</div>
												<a rel="nofollow" href="/ai/984" title="Frase" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
												</a>
											</div>
										</div><p>虚拟列表简单说原理就是只渲染当前显示屏幕区域以及前n屏和后n屏的数据,用一个单独的字段保存当前需要显示的数组(就是当前一屏+前n屏+后n屏),每次列表滚动的时候重新计算需要显示的数据,更新这个字段,页面就会相应更新了。这样就能保证页面上的元素节点数量不会太多,就可以支持大量数据的长列表需求。</p><p>更详细的原理和实现各位同学们可以自己搜一下,此处不展开。</p><p>小程序官方也有开源的虚拟列表组件:recycle-view</p><h4 data-id="heading-4">2. <code>setData</code> 可以支持颗粒更新,指定到具体的属性。</h4><p>比如加购等操作,需要更新商品右上角的小数字,可以这样写:</p><pre class="brush:php;toolbar:false;">this.setData({
    [`goodsList[${categoryIndex}][${goodsIndex}].num`]: goodsItem.num
})复制代码

3. 跟页面无关的数据不要保存在 data,不要用 setData 更新,因为 setData 会触发页面渲染。

eg:

Page({    data: {
        ...
    },    // 跟页面渲染无关的数据
    state: {        hasLogin: false,
    },
    ...
})// 更新的时候直接赋值就行this.state.hasLogin = true复制代码

PS:或者甚至不需要挂载到 page 对象下,直接用普通变量保存。

4. 图片大小优化

在长列表中图片大小如果不加限制,大量的大图会占用很多内存,有可能导致iOS客户端内存占用上升,从而触发系统回收小程序页面。除了内存问题外,大图片也会造成页面切换的卡顿。

解决办法就是根据当前显示的图片区域大小,取尺寸刚好合适(2倍-3倍图)的图片。

建议图片用CDN,一般CDN服务厂商提供图片服务的都会提供裁剪图片的接口,然后接口只返回原图链接,前端根据需要传参数裁剪图片。前端具体做法可以写公共的图片处理方法,或者自己封装图片组件。

附常用图片CDN服务商图片裁剪API文档:

  • 阿里云OSS图片缩放
  • 七牛云图片处理

5. 减少不必要的数据请求

比如在该点餐页面进入时需要获取定位,然后根据定位获取最近的门店,前面两个接口都需要请求(具体可以根据业务需求),而最后如果获取到的距离最近的门店跟上次一样,则不需要重新获取店铺详情和商品数据。

体验优化

1. 合并短时间内的多个loading提示

还是该点餐页面流程,像上文说过的,进入页面时需要获取定位接口,等定位接口返回结果了再拿定位取值去获取距离最近的店铺,最后才是请求店铺和商品数据。

这三个接口是串行的。此时如果我们每个接口都弹出一个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)
    }
}复制代码

2. 整个页面初次加载时可以使用页面loading动画或骨架屏,优化加载中体验。

3. 静默获取、更新数据

比如这个点餐页每次 onShow 都会调用定位接口和获取最近门店接口,但是不显示loading,用户就没有感知,体验比较好。

接口优化

需要关注接口的粒度控制。 因为有时候合并接口,前端可以减少一次请求,体验更好;但有时候如果接口的数据太多,响应太慢,就可以考虑是否某部分数据可以后置获取,让主要的页面内容先渲染出来,根据这个设计来拆分接口。

比如项目中的点餐页面,原来购物车数据和商品规格弹窗显示的详情数据都是在获取店铺商品接口一次性返回的,而这个接口本来由于设计需要一次返回所有商品,就会造成数据量太大,而且后端需要查询的表也更多。于是把获取购物车,和商品详情接口都拆分为单独的接口,获取店铺商品接口的响应时间就减少了,页面也能更快显示出来。

总结

其实上面提到的逻辑优化和接口优化很多都是细节,并不是太高深的技术,我们平时迭代的时候就可以注意。而体验方面的优化则需要前端同学在前端技术以外更多关注用户体验和设计方面的知识啦,而且这也是一个有追求的前端应该具备的技能……←_←

所以嘛……技术路漫漫,大家共勉吧

相关免费学习推荐:微信小程序开发教程

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2023.10.19

2768

3

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

2025.10.17

2145

12

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2025.12.29

3364

9

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

2026.01.19

160

15

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

2094

6

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

2023.06.20

1857

8

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.07.28

910

5

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1154

5

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

2023.08.17

7910

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.4万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 131.8万人学习