android工程师学习微信小程序笔记⑫ 小程序性能优化

小程序官方文档

当开发完一个小程序之后,性能优化就是一项长期的工作,那么我们就得了解一下如何做性能优化。

启动

在小程序启动时,微信会为小程序展示一个固定的启动界面,界面内包含小程序的图标、名称和加载提示图标。

此时,微信会在背后完成几项工作:

  • 下载小程序代码包
  • 加载小程序代码包
  • 初始化小程序首页。

在这里插入图片描述

代码包下载

在某个小程序第一次启动时,微信需要下载小程序代码包。此后,如果小程序代码包未更新且还被保留在缓存中,则下载小程序代码包的步骤会被跳过。下载到的小程序代码包不是小程序的源代码,而是编译、压缩、打包之后的代码包。代码包大小可以在开发者工具的“详情”栏中找到。

从开发者的角度看,控制代码包大小有助于减少小程序的启动时间

  • 精简代码,去掉不必要的WXML结构和未使用的WXSS定义。
  • 减少在代码包中直接嵌入的资源文件。
  • 压缩图片,使用适当的图片格式。

分包加载流程

如果小程序比较复杂,优化后的代码总量可能仍然比较大,此时可以采用分包加载的方式进行优化。

一般情况下,小程序的代码将打包在一起,在小程序启动时一次性下载完成。采用分包时,小程序的代码包可以被划分为几个:一个是“主包”,包含小程序启动时会马上打开的页面代码和相关资源;其余是“分包”,包含其余的代码和资源。这样,小程序启动时,只需要先将主包下载完成,就可以立刻启动小程序。这样就可以显著降低小程序代码包的下载时间。

代码包加载

微信会在小程序启动前为小程序准备好通用的运行环境。这个运行环境包括几个供小程序使用的线程,并在其中完成小程序基础库的初始化,预先执行通用逻辑,尽可能做好小程序的启动准备。这样可以显著减少小程序的启动时间。

小程序的代码包被下载(或从缓存中读取)完成后,小程序的代码会被加载到适当的线程中执行。

此时,所有app.js、页面所在的JS文件和所有其他被require的JS文件会被自动执行一次,小程序基础库会完成所有页面的注册。

在小程序代码调用Page构造器的时候,小程序基础库会记录页面的基础信息,如初始数据(data)、方法等。

需要注意的是,如果一个页面被多次创建,并不会使得这个页面所在的JS文件被执行多次,而仅仅是根据初始数据多生成了一个页面实例(this),在页面JS文件中直接定义的变量,在所有这个页面的实例间是共享的。

页面层级准备

在视图层内,小程序的每一个页面都独立运行在一个页面层级上。小程序启动时仅有一个页面层级,每次调用wx.navigateTo,都会创建一个新的页面层级;相对地,wx.navigateBack会销毁一个页面层级。

对于每一个新的页面层级,视图层都需要进行一些额外的准备工作。在小程序启动前,微信会提前准备好一个页面层级用于展示小程序的首页。除此以外,每当一个页面层级被用于渲染页面,微信都会提前开始准备一个新的页面层级,使得每次调用wx.navigateTo都能够尽快展示一个新的页面。

页面层级的准备工作分为三个阶段。第一阶段是启动一个WebView,在iOS和Android系统上,操作系统启动WebView都需要一小段时间。第二阶段是在WebView中初始化基础库,此时还会进行一些基础库内部优化,以提升页面渲染性能。第三阶段是注入小程序WXML结构和WXSS样式,使小程序能在接收到页面初始数据之后马上开始渲染页面(这一阶段无法在小程序启动前执行)。

在这里插入图片描述
对于wx.redirectTo,这个调用不会打开一个新的页面层级,而是将当前页面层级重新初始化:重新传入页面的初始数据、路径等,视图层清空当前页面层级的渲染结果然后重新渲染页面。

数据通信

在每个小程序页面的生命周期中,存在着若干次页面数据通信。逻辑层向视图层发送页面数据(data和setData的内容),视图层向逻辑层反馈用户事件。

页面初始数据通信

在小程序启动或一个新的页面被打开时,页面的初始数据(data)和路径等相关信息会从逻辑层发送给视图层,用于视图层的初始渲染。

Native层会将这些数据直接传递给视图层,同时向用户展示一个新的页面层级,视图层在这个页面层级上进行界面绘制。

视图层接收到相关数据后,根据页面路径来选择合适的WXML结构,WXML结构与初始数据相结合,得到页面的第一次渲染结果。

在这里插入图片描述
分析这个流程不难得知:页面初始化的时间大致由页面初始数据通信时间和初始渲染时间两部分构成。其中,数据通信的时间指数据从逻辑层开始组织数据到视图层完全接收完毕的时间,数据量小于64KB时总时长可以控制在30ms内。传输时间与数据量大体上呈现正相关关系,传输过大的数据将使这一时间显著增加。因而减少传输数据量是降低数据传输时间的有效方式。

在这里插入图片描述

更新数据通信

初始渲染完毕后,视图层可以在开发者调用setData后执行界面更新。

在数据传输时,逻辑层会执行一次JSON.stringify来去除掉setData数据中不可传输的部分,之后将数据发送给视图层。同时,逻辑层还会将setData所设置的数据字段与data合并,使开发者可以用this.data读取到变更后的数据

因此,为了提升数据更新的性能,开发者在执行setData调用时,最好遵循以下原则:

  • 不要过于频繁调用setData,应考虑将多次setData合并成一次setData调用;
  • 数据通信的性能与数据量正相关,因而如果有一些数据字段不在界面中展示且数据结构比较复杂或包含长字符串,则不应使用setData来设置这些数据;
  • 与界面渲染无关的数据最好不要设置在data中,可以考虑设置在page对象的其他字段下。

用户事件通信

视图层会接受用户事件,如点击事件、触摸事件等。用户事件的通信比较简单:当一个用户事件被触发且有相关的事件监听器需要被触发时,视图层会将信息反馈给逻辑层。如果一个事件没有绑定事件回调函数,则这个事件不会被反馈给逻辑层。视图层中有一套高效的事件处理体系,可以快速完成事件生成、冒泡、捕获等过程。

视图层将事件反馈给逻辑层时,同样需要一个通信过程,通信的方向是从视图层到逻辑层。因为这个通信过程是异步的,会产生一定的延迟,延迟时间同样与传输的数据量正相关,数据量小于64KB时在30ms内。降低延迟时间的方法主要有两个。

  • 去掉不必要的事件绑定(WXML中的bind和catch),从而减少通信的数据量和次数;
  • 事件绑定时需要传输target和currentTarget的dataset,因而不要在节点的data前缀属性中放置过大的数据。

在这里插入图片描述

视图层渲染

视图层在接收到初始数据(data)和更新数据(setData数据)时,需要进行视图层渲染。在一个页面的生命周期中,视图层会收到一份初始数据和多份更新数据。收到初始数据时需要执行初始渲染,每次收到更新数据时需要执行重渲染。

初始渲染

初始渲染发生在页面刚刚创建时。初始渲染时,将初始数据套用在对应的WXML片段上生成节点树。节点树也就是在开发者工具WXML面板中看到的页面树结构,它包含页面内所有组件节点的名称、属性值和事件回调函数等信息。最后根据节点树包含的各个节点,在界面上依次创建出各个组件。

在这里插入图片描述
在这整个流程中,时间开销大体上与节点树中节点的总量成正比例关系。因而减少WXML中节点的数量可以有效降低初始渲染和重渲染的时间开销,提升渲染性能

重渲染

初始渲染完毕后,视图层可以多次应用setData的数据。每次应用setData数据时,都会执行重渲染来更新界面。

初始渲染中得到的data和当前节点树会保留下来用于重渲染

每次重渲染时,将data和setData数据套用在WXML片段上,得到一个新节点树。然后将新节点树与当前节点树进行比较,这样可以得到哪些节点的哪些属性需要更新、哪些节点需要添加或移除。最后,将setData数据合并到data中,并用新节点树替换旧节点树,用于下一次重渲染。

在这里插入图片描述
在进行当前节点树与新节点树的比较时,会着重比较setData数据影响到的节点属性。因而,去掉不必要设置的数据、减少setData的数据量也有助于提升这一个步骤的性能。

原生组件通信

一些原生组件支持使用context来更新组件。不同于setData,使用context来更新组件并不会涉及到重渲染过程,数据通信过程也不同。在setData的数据通信流程中,数据从逻辑层经过native层转发,传入视图层的WebView,再经过一系列渲染步骤之后传入组件。而使用context时,数据从逻辑层传到native层后,直接传入组件中,这样可以显著降低传输延迟。

在这里插入图片描述
在具体通信过程上,因为context的方法繁多,通信方式相对于setData更复杂。不过基础库会对context方法调用时的通信进行封装优化,通常开发者不需要关心这个问题。

原创文章 152 获赞 1013 访问量 40万+

猜你喜欢

转载自blog.csdn.net/dpjcn1990/article/details/105767243