Tout d'abord, présentons brièvement le projet, qui est une application de commande relativement conventionnelle.
L'interface est la suivante :
La gauche est le menu des catégories, la droite est la longue liste, il y a plusieurs catégories de produits, vous peut continuer à défiler après avoir parcouru une seule catégorie. Passez à la catégorie suivante et l'état sélectionné du menu de catégorie sur la gauche passera à la catégorie affichée dans la liste de produits actuelle.
Afin d'offrir une meilleure expérience utilisateur et en référence à Meituan et à d'autres applets de commande, les données de cette liste de produits sont renvoyées en une seule fois. Le problème rencontré actuellement est que lorsque le nombre de produits est important, le premier temps de rendu est très long et la page se fige.
Xiaosheng bb : En fait, le code original (pour des raisons historiques) était trop mal écrit... OTL
Mettez d'abord une photo
Whisper bb : Je ne supporte même plus le mini programme, donc je dois vous prévenir
Développeur WeChat les outils vous ont prévenu, et l'invite contient également l'emplacement du code spécifique, donc la clé est celle-ci setData
! ! !
Nous pouvons d'abord jeter un œil à quelques suggestions officielles pour les performances des mini-programmes et setData
l'optimisation. (developers.weixin.qq.com/miniprogram…)
Pratique spécifique :
setData
Vous ne pouvez pas transmettre trop de données à la fois. Si la liste est trop longue, vous pouvez. restituez-le séparément [par exemple Convertissez en un tableau bidimensionnel et restituez un tableau à chaque fois dans la boucle]. v1 : Version simple et approximative
// 每次渲染一个分类// 假设goodsList是一个二维数组goodsList.forEach((item, index) => { this.setData({ [`goodsList[${index}]`]: item }) })复制代码
Il y a un problème lors de l'écriture comme ci-dessus Le rendu du premier écran de la page est rapide, mais lorsque vous cliquez sur l'opération de la page (comme par exemple). le bouton d'achat, etc.), la page se fige et attend une réponse. Le retour d'opération est sérieusement retardé.
En fait, c'est parce que ce cycle réduit le nombre de setData
simples, mais le transforme en un cycle de setData
multiples. On voit que le premier écran est affiché, mais en fait d'autres catégories (. D'autres tableaux) sont toujours en cours de rendu, le thread est toujours occupé, le thread JS a compilé et exécuté le rendu, l'événement de clic ne peut pas être transmis à la couche logique à temps et la couche logique ne peut pas transmettre les résultats du traitement de l'opération à la couche de visualisation. dans le temps.
v2 : version timer hack
Puisque le thread js est occupé à rendre, nous pouvons le forcer à s'arrêter en premier. Il existe donc une version timer hack de la 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);复制代码
Maintenant, le premier problème de vitesse de rendu de l'écran a été résolu, et le problème de réponse retardée du clic sur les boutons a également été résolu. C'est juste que le code est un peu hacky, ce qui me fait souffrir d'un trouble obsessionnel-compulsif
v3 : Big killer - liste virtuelle
Le principe simple d'une liste virtuelle est de ne restituer que le courant zone d'écran d'affichage et les n écrans avant et arrière n écrans Utilisez un champ séparé pour enregistrer le tableau actuel qui doit être affiché (c'est-à-dire l'écran actuel + les n écrans précédents + les n écrans suivants Les données qui doivent). à afficher est recalculé à chaque défilement de la liste. Mettez à jour ce champ et la page répondra en conséquence. Cela garantit que le nombre de nœuds d'éléments sur la page n'est pas trop grand et peut prendre en charge les exigences de longues listes pour de grandes quantités de données.
Les étudiants peuvent rechercher eux-mêmes des principes et des mises en œuvre plus détaillés, qui ne seront pas développés ici.
Le mini-programme officiel dispose également d'un composant de liste virtuelle open source : recycle-view
setData
peut prendre en charge les mises à jour des particules et spécifier des attributs spécifiques. Par exemple, si vous devez mettre à jour le petit numéro dans le coin supérieur droit du produit pour des opérations telles que des achats supplémentaires, vous pouvez l'écrire comme ceci :
this.setData({ [`goodsList[${categoryIndex}][${goodsIndex}].num`]: goodsItem.num })复制代码
data
, n'utilisez pas setData
Mis à jour car setData
déclenche le rendu de la page. par exemple :
Page({ data: { ... }, // 跟页面渲染无关的数据 state: { hasLogin: false, }, ... })// 更新的时候直接赋值就行this.state.hasLogin = true复制代码
PS : Ou vous n'avez même pas besoin de le monter sous l'objet page
, enregistrez-le simplement directement avec une variable ordinaire.
Si la taille de l'image dans la longue liste n'est pas limitée, un grand nombre d'images volumineuses occuperont beaucoup de mémoire, ce qui peut entraîner une utilisation de la mémoire du client iOS. pour augmenter, déclenchant ainsi la page du mini programme de recyclage du système. En plus des problèmes de mémoire, les images volumineuses peuvent également entraîner des décalages de changement de page.
La solution est de prendre une photo avec juste la bonne taille (2x-3x l'image) en fonction de la taille de la zone d'image actuellement affichée.
Il est recommandé d'utiliser un CDN pour les images. Généralement, les fournisseurs de services CDN qui fournissent des services d'image fourniront une interface pour recadrer les images, puis l'interface ne renvoie que le lien de l'image d'origine et le frontal transmet les paramètres. pour recadrer l'image selon vos besoins. L'approche frontale spécifique peut consister à écrire une méthode de traitement d'image publique ou à encapsuler vous-même le composant d'image.
Vous trouverez ci-joint le document API de recadrage d'image des fournisseurs de services CDN d'images couramment utilisés :
比如在该点餐页面进入时需要获取定位,然后根据定位获取最近的门店,前面两个接口都需要请求(具体可以根据业务需求),而最后如果获取到的距离最近的门店跟上次一样,则不需要重新获取店铺详情和商品数据。
还是该点餐页面流程,像上文说过的,进入页面时需要获取定位接口,等定位接口返回结果了再拿定位取值去获取距离最近的店铺,最后才是请求店铺和商品数据。
这三个接口是串行的。此时如果我们每个接口都弹出一个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,用户就没有感知,体验比较好。
需要关注接口的粒度控制。 因为有时候合并接口,前端可以减少一次请求,体验更好;但有时候如果接口的数据太多,响应太慢,就可以考虑是否某部分数据可以后置获取,让主要的页面内容先渲染出来,根据这个设计来拆分接口。
比如项目中的点餐页面,原来购物车数据和商品规格弹窗显示的详情数据都是在获取店铺商品接口一次性返回的,而这个接口本来由于设计需要一次返回所有商品,就会造成数据量太大,而且后端需要查询的表也更多。于是把获取购物车,和商品详情接口都拆分为单独的接口,获取店铺商品接口的响应时间就减少了,页面也能更快显示出来。
其实上面提到的逻辑优化和接口优化很多都是细节,并不是太高深的技术,我们平时迭代的时候就可以注意。而体验方面的优化则需要前端同学在前端技术以外更多关注用户体验和设计方面的知识啦,而且这也是一个有追求的前端应该具备的技能……←_←
所以嘛……技术路漫漫,大家共勉吧
相关免费学习推荐:微信小程序开发教程
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!