草庐IT

微信小程序开发04 性能优化:借助微信开发者工具提升小程序性能

办公模板库 素材蛙 2023-04-11 原文

你好,我是周俊鹏。

前几节课我们分别从架构层(双线程模型)、链路层(授权模型)、和应用层(自定义组件)三个角度学习了小程序的技术要点。它们能帮你完成一个微信小程序的基本业务逻辑和交互逻辑。

逻辑的第一诉求必然是支撑功能,对于端侧应用来说,功能是一方面,功能之外的用户体验也至关重要,良好的体验能加强用户对于应用的认同甚至依赖。小程序作为一种端侧应用,与其他应用端( Web/App )一样需要关注用户体验。用户体验有两个核心的要素:一是设计;二是性能。作为技术研发,我们关注的重点在于如何提升小程序的性能。

在我看来,掌握底层的技术原理能帮我们写出性能更好的代码,不过理论在转化为实践时往往不是非常直观,所以在掌握理论的同时学会利用工具更为重要。

今天我就结合微信开发者工具(简称微信 IDE)提供的小程序评分功能,来讲解一款高性能小程序背后的优化技巧。

微信 IDE 的小程序评分功能位于调试器-> Audits 面板中:

点击“运行”之后,微信 IDE 会对当前的小程序项目进行评测(包括代码层面的检测、通过记录用户交互行为的体验检测)。最终从性能、体验和最佳实践三个维度分别打分以及综合分:

  • 性能评分是通过对页面渲染、网络、JS 脚本等方面的评估综合得来的;

  • 体验评分是从设计和交互等方面的评估而来,由于设计和交互存在一定的主观因素,所以体验的评分权当建议;

  • 最佳实践涉及的方面更宽泛,除了代码编写方面的建议(比如 01 讲我们提到尽量减少或聚合 setData 的调用),还有安全(比如尽量使用 HTTPS 增强安全性)和用户体验(比如适配不同宽度的屏幕)方面的建议。

除了性能评分外,微信 IDE 给出的最佳实践方案中也有一部分与性能相关。接下来我就提取所有和性能相关的部分,剖分小程序性能优化的一些具体措施(用户体验跟这节课无关,我就不讲了)。

小程序性能优化的具体维度

微信 IDE 对小程序性能进行评分有以下几个维度(微信 IDE 的截图比较大,字体也比较小,所以我就不放截图了):

  1. 避免过大的 WXML 节点数目

  2. 避免执行脚本的耗时过长的情况

  3. 避免首屏时间太长的情况

  4. 避免渲染界面的耗时过长的情况

  5. 对网络请求做必要的缓存以避免多余的请求

  6. 所有请求的耗时不应太久

  7. 避免 setData 的调用过于频繁

  8. 避免 setData 的数据过大

  9. 避免短时间内发起太多的图片请求

  10. 避免短时间内发起太多的请求

其实这 10 个性能的评分标准并不仅仅适用于微信小程序,有几条(2~6、9和10)是前端开发领域的通用性能指标,而且如果你用过 Vue/React 之类的MVVM 框架,以上指标可以全部应用到基于 Vue/React 框架开发的 Web 应用程序中。接下来我们一条条地剖析这几个指标对于性能优化的意义以及对应的解决方案。

当然了,有些条目的优化方向是一致的(比如 7 和 8 是为了提高渲染性能),在剖析过程中我们会进行必要的聚合,这样更利于从全局的角度了解这些性能评分标准之间的关联。

避免过大的 WXML 节点数目

WXML 是基于 HTML 的一种 DSL(Domain Specific Language,领域专属语言),除了原生组件(比如 Camera 相机组件)以外,常规组件最终会被小程序的渲染线程(还记得 01 讲的渲染线程是什么吗?不记得的话要及时复习。)通过 WebView 渲染为 HTML ,所以从性能优化的角度上,HTML 的大部分性能优化方案均适用于 WXML,尽量减少节点数目就是方案之一。

节点数目会影响渲染性能,要理解这句话,你要对浏览器的渲染流程有大概了解,来看下面这张图:

HTML 是 XML 的变体,在渲染的时候首先会被浏览器内核解析为 DOM 树,这是一种树形结构,然后会解析每个节点标签的类型、属性等要素,最后与 JavaScript 脚本和 CSS 结合起来进而在经过布局和绘制完成整个渲染流程。

理论上 HTML 的节点数目和深度是没有限制的,但是从浏览器的渲染流程中不难看出,DOM 树的结构越复杂,渲染的管线就会越慢。我们再回想一下 01 讲的内容,当渲染线程执行的同时,逻辑线程是被阻塞的,也就是说如果渲染线程长时间占用了队列,这期间浏览器处于无法响应用户交互行为的“假死”状态,这对于用户体验是致命的。

降低节点数目对于性能优化的另外一个原因,是与小程序 /Vue/React 这种 MVVM 框架的 DOM更新机制有关。这类框架在更新 UI 时不直接操作 DOM ,而是使用 VDOM( Virtual DOM,虚拟 DOM )技术来实现,VDOM 的高性能来源于高效的 Diff 算法,在内存中对 VDOM 树结构进行对比后提取差异点再映射到真实 DOM 中。

而你不用关注 Diff 算法的细节,只需要知道它是基于树这种数据结构进行的,而树结构的复杂度会直接影响算法的执行耗时。所以如果你的小程序节点数目过多或者层次太深,那么在调用setData 更新 UI 时就会给 CPU 和内存过多的压力,进而可能造成小程序的假死。

避免执行脚本的耗时过长

执行脚本的耗时过长对于性能的不良影响主要体现在两个时期:

  • 第一是在小程序加载完成后的首次渲染期间;

  • 第二是小程序运行过程中的处理用户交互时期。

JavaScript 脚本对小程序首次渲染的影响与浏览器环境下 <script> 标签对 HTML 渲染的影响类似,虽然小程序中不允许使用 <script> 标签,双线程模型下 JavaScript 脚本也并不会完全阻塞 UI 线程的行为,但是逻辑线程执行 JavaScript 代码时仍旧是单线程的,通过任务队列管理代码的有序执行。如果某一段 JavaScript 代码逻辑占时太长,造成任务队列过长,最终会影响小程序在响应用户交互行为上的长延时或卡顿。

避免首屏时间太长

加快首屏的加载时间是前端开发领域最核心的目标之一,从用户打开 Web 网站或小程序的时刻为计时起点,屏幕内容渲染完成为计时终点,起终点之间的时长即为首屏时间。

影响首屏时间的因素非常多(比如 DNS 解析耗时、TCP 链接的建立耗时……)对于小程序开发者来说,有些因素是不可控的(比如 DNS 解析),那么在可控的众多因素当中,最核心的两个优化方向是:

  • 代码优化;

  • 网络优化。

代码方向的优化措施重点关注这样几点:

  • 降低 WXML 的结构复杂度,比如节点个数和深度;

  • 降低首次渲染的数据规模,首次渲染只包含核心数据,非核心数据的渲染可推迟到首屏渲染完成之后进行;

  • 从设计和交互的角度出发,在实际内容被渲染之前展示友好的 loading 效果。

而网络方向的优化核心是为了降低 RTT( Road-Trip Time,往返时延),也就是微信 IDE 给出的“6.所有请求的耗时不应太多”这条建议。由于小程序的所有资源均托放在微信的服务器,所以不存在 CDN 和 DNS 优化问题,对于开发者来说,降低 RTT 最有效的两个措施是:

  • 减少网络请求所携带的数据体积,这是最直观的网络优化方案;

  • 提高服务器处理网络请求的速度,这一点是对服务端的要求,除了服务端代码本身的性能以外,当用户量上升到一定规模之后,还需要服务器有处理高并发的能力。对于专注于端侧的传统前端和小程序开发者来说,这些知识是相对陌生的,往往需要后端的同学配合完成。这也是云开发相较于传统开发模式的主要优势之一,使用云开发可以让端侧的开发者也能够开发出弹性伸缩、高并发、高 QPS 处理的服务层(更多云开发相关的内容我会在模块四详细讲解,这里就不多说了)。

避免渲染界面的耗时过长的情况

这是一条综合性能指标,渲染主要包括两个角度:一是首屏的渲染时间(上一条讲过了);二是小程序运行期间的界面更新所需的渲染时间,我们不妨称之为动态渲染。

动态渲染是由 JavaScript 脚本中调用 setData 更新数据所触发,所以优化动态渲染的切入点便一目了然:优化 setData。至于具体的优化方案,便是微信 IDE 给出的两点建议:

  • 避免 setData 的调用过于频繁。频繁调用 setData 会造成逻辑线程与渲染线程之间过多的通信,01讲我们提到双线程之间的通行需要借助微信原生平台作转发,中间必然是有一定的性能损耗和时延。除此之外,渲染线程在接收到逻辑线程传递的数据之后,需要进行解析、VDOM 对比、更新 UI 等一套管线流程,在前一条流程执行完结之前,后面的数据只能排队等待执行。所以频繁调用 setData 就会造成队列加长,用户交互行为触发的 UI 更新就会缓慢甚至可能由于计算量太大造成卡顿。

  • 避免 setData 的数据量太大。频繁调用 setData 会造成队列中的任务太多,而如果 setData 的数据量太大,则会造成单个任务的处理耗时加长。与上一条相比,一个是任务数量过多,一个是单个任务过重,两者最终对于性能产生的负面影响是一致的。此外,由于双线程之间需要借助微信原生平台转发,所以 setData 数据量过大也会造成通信时延的加长。

对网络请求做必要的缓存以避免多余的请求

小程序的资源文件托管在微信的服务器,所以小程序开发者不需要关注前端开发领域中对于静态资源的 HTTP 缓存策略,这件事情微信会帮助开发者完成。

这一条建议所指的是在代码层面,将部分重复使用的网络请求结果在代码或 storage 中进行合理缓存以实现复用,对于使用同一个网络请求结果的代码可以直接从缓存中读取,进而减少了不必要的网络请求个数。每次网络请求不论时间长短,均需要用户等待,减少网络请求的个数相当于减少了用户等待时间,提升了用户体验。

避免短时间内发起太多的图片请求

这一条与微信 IDE 给出的另一条建议“10.避免短时间内发起太多的请求”的方向是一致的,均是为了解决过多 HTTP 请求造成用户等待时间过长的问题。图片资源相对特殊的一个特点是体积较大,前端领域最早的懒加载方案便是主要针对图片资源,所以图片资源的请求对性能的影响更加直观一些。

目前前端和小程序领域中使用的仍旧是 HTTP 1.1 协议,一个 TCP 链接同时只能处理一个 HTTP 请求,在前一个请求得到服务器的响应之后才会发起第二个请求,如果同一时间的 HTTP 请求太多就会产生排队。

浏览器为了应对这种问题,提供了建立多个 TCP 连接以实现并行发送 HTTP 请求的目的,目前市面上的浏览器最多支持同时建立 4~8 个 TCP 连接。也就是说,最多可以同时处理 4~8 个HTTP 请求。如果同一时刻需要发送的 HTTP 请求数量远大于这个数字,那么还是会产生排队。前面的内容我们重复地提到了“排队”一词,不论是线程间的通信排队、任务队列的排队、还是 HTTP 请求的排队,这些行为都是需要用户等待的,对于用户的切身体验来说,便是响应缓慢甚至卡顿。

总结

通过以上内容我们不难看出来,微信 IDE 的体验评分功能给出的性能优化方案跟 Web 应用的性能优化方案大同小异,尤其是使用 Vue/React 这类 MVVM 框架的 Web 应用。

微信 IDE 给出的这些建议能够很好地帮助我们写出性能更好的小程序代码以及搭建高性能的前后端架构,不过现实中小程序的业务类型多种多样,这些性能优化的方案也只是从单纯的技术角度出发,对于不同类型的业务来说,通常会有一些专属的优化措施,比如视频类小程序的分片加载、游戏类小程序的 canvas 优化等等。所以今天的课后作业便是:结合你以前做过的小程序项目类型,想一想有没有对应的专属性能优化方案。

截止到这节课,模块一的内容便完结了,这个模块的内容偏底层,了解这些底层知识能够帮助你写出更好的代码。模块二我们将从效率提升的角度,讲解如果将小程序的开发模式从小作坊迈向工程化,共同期待吧。


精选评论

**波:

setdata加载的数据量有没有一个大概的量化数据?例如50个左右开始有影响,80个就要减少

    讲师回复:

    没有具体的数字,setData的性能除了受调用频率影响,还会受数据体积的影响。所以最优解是需要频繁调试得出的一个经验值。

**艺:

小程序使用的是http1.1 ?

    讲师回复:

    目前还不支持http 2.0,官方有规划了,敬请期待。

**宁:

有点疑问,setData的次数和setData的数据本身数据量的大小感觉大概率是成反比的即setData次数多,就可以通过发多次来减少setData本身发送数据量大的问题,反之同理。请问大佬这方面有什么建议吗?

    讲师回复:

    setData是一条逻辑链路,影响执行效率的并不仅仅是数据量的大小这一个因素,setData的调用频率和数据体量之间的抉择并没有绝对的最优解,虽然这么说有点虚,但现实中确实是需要不断地调优才能得到一个平衡点。

**8686:

您好,我的一个小程序使用云开发存储了多个音频文件,集合里面记录的都是云ID,我在一个页面中需要连续播放一组音频文件,会产生卡顿的现象(第一个音频播放完,开始播放第二个音频,然而第二个音频数据还没有加载完成),请问使用云存储如何能避免以上卡顿现象,或者如何实现预加载云资源?

    讲师回复:

    这个是特定业务场景下的逻辑,并不属于云开发要解决的问题,云开发只提供基础的能力。不论是云存储还是常规的存储方法,它们本身都只是原子能力,你说的这种预加载场景是在这些基础能力之上的业务逻辑。

有关微信小程序开发04 性能优化:借助微信开发者工具提升小程序性能的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  9. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  10. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

随机推荐