微信小程序渲染性能调优总结
网页性能优化是前端开发的热门话题。由于微信小程序的双线程架构设计,性能优化的方法不同于传统的H5应用。今天主要介绍微信小程序页面的双线程架构对页面渲染的一些影响以及一些渲染性能调优策略。为了描述方便,我们简称微信小程序为小程序。
小程序的双线程架构
与传统浏览器网页最大的区别是小程序基于双线程模型。在这种架构下,WebView在小程序的渲染层作为渲染载体,而JS脚本则由逻辑层独立的JsCore线程运行。双方都没有直接数据共享的通道,所以渲染层和逻辑层之间的通信应该由Native JSBrigde来传递。
小程序更新视图数据的通信流程
每当小程序视图数据需要更新时,逻辑层就会调用小程序宿主环境提供的setData方法,将数据从逻辑层转移到视图层,经过一系列的渲染步骤后完成UI视图更新。完整的沟通过程如下:
小程序逻辑层调用宿主环境的setData方法。逻辑层执行JSON.stringify将待传输的数据转换成字符串并拼接成特定的JS脚本,然后通过evaluateJavascript执行脚本将数据传输到渲染层。渲染层收到后,WebView JS线程会编译脚本,获取需要更新的数据,进入渲染队列,等待WebView线程空闲时渲染页面。当WebView线程开始渲染时,要更新的数据将被合并到视图层中保留的原始数据中,新数据将被应用到WXML片段中,以获得新的虚拟节点树。在新的虚拟节点树和当前节点树之间进行差异比较后,差异会更新到用户界面视图。同时,用新节点树替换旧节点树,以便下次重新渲染。渲染性能问题的一些原因
在上述通信流程中,一些不恰当的操作可能会影响页面渲染的性能:
SetData传递了很多新数据
数据的传输会经历跨线程传输和脚本编译的过程。当数据量过大时,会增加脚本编译的执行时间,占用WebView JS线程。
下图是一组测试统计:在相同的网络环境下,每个模型分别对1KB、2KB和3KB数据进行setData操作所消耗的时间。
从图中可以看出,setData的数据传输量越大,数据传输所消耗的时间就越长。
频繁执行设置数据操作
频繁执行setData将使WebView JS线程忙于脚本编译、节点树比较计算和页面呈现。结果是:
页面呈现结果有一定的延迟。当用户触发页面事件时,WebView JS线程繁忙,用户事件没有及时传输到逻辑层,导致反馈延迟。页面节点太多
在初始页面渲染中,渲染树的构建、节点几何信息的计算以及绘制节点到屏幕的时间成本都与页面节点数呈正相关。页面节点越多,渲染时间越长。每次执行setData来更新视图时,WebView JS线程都应该遍历节点树来计算新旧节点之间的差异。当页面节点数较大时,计算的时间成本较大。减少节点树的数量可以有效降低重渲染的时间成本。渲染性能优化
根据渲染性能问题的原因,我们可以制定一些优化策略来避免性能问题。
SetData优化
SetData作为逻辑层和视图层的通信媒介,是最容易造成渲染性能瓶颈的API。在使用setData时,我们应该遵循一些规则,以尽可能避免性能问题:
减少了数据集数据传输量
只传输视图层使用的数据,其他JS环境使用的数据存储在data对象之外。合理利用局部更新。SetData支持使用数据路径更新对象的本地字段。我们可能会遇到这样的场景:列表列表是从后台获取并显示在页面上的数据。当列表列表中第一项数据的src字段需要更新时,我们通常会从后台获取一个新的列表列表,并执行setData来更新整个列表列表。//后台获取列表数据const list=request sync();//更新整个列表this . setdata({ list });事实上,当只需要更新单个字段时,我们可以这样写,以避免更新整个列表:
//后台获取列表数据const list=request sync();//在本地更新列表this . setdata({ ' list[0])。src' :list [0]。src });降低setData的执行频率
在不影响业务流程的前提下,合并执行多个setData调用,降低了线程间的通信频率。在频繁触发的用户事件(如PageScroll、Resize事件)中需要调用setData时,合理使用函数去抖和函数节流可以减少setData的执行次数。功能去抖:该功能在触发n秒后执行一次。如果该功能在n秒内被重复触发,时间将被重新计算。
功能节流:该功能每单位时间只触发一次。如果在同一单位时间内多次触发该功能,则只会生效一次。
除了让开发人员自觉遵循规则减少setData的数据传输量和执行频率,我们还可以设计一个diff算法对setData进行重新打包,这样在执行setData之前,就可以通过diff将需要更新的数据与原始数据进行对比,计算出数据差异补丁对象。判断补丁对象是否为空,如果为空,则跳过更新的执行,否则对补丁对象执行setData操作,以减少数据传输量和执行setData的频率。
//setData被重新打包成一个新的方法,在更新数据之前对新旧数据进行diff比较,然后执行setData方法这个。update=(data)={ return new promise((解析,拒绝)={const result=diff (data,this。数据);if(!Object.keys(结果)。长度){ resolve(null);返回;} this.setData(结果,()={ resolve(结果);});});}当然,这里可以直接参考现成的高性能小程序setData diff算法
具体流程如下:
充分利用定制组件
小程序定制组件的实现由小程序设计的Exparser框架支持。框架实现的自定义组件的组件模型类似于Web组件标准的Shadow DOM:
页面引用自定义组件后,在初始化页面时,Exparser会根据自定义组件的注册信息创建页面实例并实例化组件,然后根据组件的数据数据和WXML构造一个独立的Shadow Tree,并追加到页面Composed Tree中。创建的影子树有自己独立的逻辑空间、数据、样式环境和集合数据调用:
基于自定义组件的Shadow DOM模型设计,可以封装一些功能模块(如倒计时、进度条等。)在需要高频更新setData的页面中插入自定义组件并嵌入到页面中。当这些自定义组件视图需要更新时,执行组件自己的setData,新旧节点树的比较计算和渲染树的更新都限制在组件中有限的节点数内,有效降低了渲染时间开销。
下图是从微防护小程序WeDrive首页倒计时模块中提取自定义组件前后setData更新时间的对比:
从图中可以看出,使用定制组件后,倒计时模块setData的平均渲染时间明显下降,在低端安卓机中的实际体验会明显感觉更加流畅。
当然,并不是说使用的定制组件越多越好。对于添加到页面中的每个新的定制组件,Exparser都需要再管理一个组件实例,内存消耗会更大。当内存占用上升到一定程度,可能会导致iOS回收一些WKWebView,安卓机器体验会变得更加卡壳。所以要合理使用自定义组件,在页面设计中注意不要滥用标签。
摘要
小程序的双线程架构决定了数据通信优化将是性能优化的一个重点。上述优化建议只是优化渲染性能的一部分。想要让页面体验更流畅,就要熟悉小程序框架的底层原理,根据小程序框架的特点编写“契合”的前端代码。
参考材料
微信小程序官方开发指南
适用于小型程序的高性能setData diff算法
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:微信小程序渲染性能调优总结是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。