网页性能优化是前端一个老生常谈的话题,但微信小程序由于双线程的架构设计,跟传统 Web
页面不太同样。因此,今天来探究下微信小程序内的性能优化问题。html
首先,要问你们一个问题:从打开微信小程序到首页展现在你们面前,要通过哪些过程?(能够类比与前端另外一个常见问题:Web
页面从输入 url
到页面展现具体通过了哪些过程)前端
相信你们对 Web
页面的展示过程很是清楚。那么小程序呢,简要地说,小程序要历经下面几个启动过程:算法
小程序初始化: 微信初始化小程序环境:包括 Js
引擎和 WebView
进行初始化,并注入公共基础库。 这步是微信作的,在用户打开小程序以前就已经准备好了,是小程序运行环境预加载。小程序
下载小程序代码包 对小程序业务代码包进行下载:下载的不是小程序的源代码,而是编译、压缩、打包以后的代码。微信小程序
加载小程序代码包 对下载完成对代码包进行注入执行。 此时,app.js
、页面所在的 Js
文件和全部其余被 require
的 Js
文件会被自动执行一次,小程序基础库会完成全部页面的注册。浏览器
初始化小程序首页 拉取数据,从逻辑层传递到视图层,进行渲染。缓存
既然清楚了小程序的启动过程,那咱们就能够针对其中的每个环节进行性能分析和优化。性能优化
因为这个环节是微信执行的,属于小程序底层的执行耗时,因此咱们开发者没法操控。已知的是:iOS
初始化比 Android
快一些。bash
通常来讲:下载环节是耗时比较长的环节。 对低于 1MB
的代码包,其下载时间能够控制在 929ms(iOS)、1500ms(Android)内。 那么提高下载性能最关键的一点是:控制包的大小。微信
常见的控制代码包大小的方法以下:
若是小程序比较复杂,优化后的代码总量仍然较大,此时能够采用分包加载的方式进行优化。
其原理是: 通常状况下,小程序的代码将打包在一块儿,在小程序启动时一次性下载完成。 采用分包时,小程序的代码包能够被划分为几个:一个是“主包”,包含小程序启动时会立刻打开的页面代码和相关资源;其他是“分包”,包含其他的代码和资源。 这样,小程序启动时,只须要先将主包下载完成,就能够马上启动小程序。这样就能够显著下降小程序代码包的下载时间。
可是这个时候又出现了另外一个问题,在咱们访问分包页面时,须要先下载完分包代码,才能打开分包页面,这是能感受到到明显卡顿,体验也是比较差的。
咱们能够经过配置 preloadRule
进行分包预加载:打开首页,加载完主包后,静默加载其余分包。
除了普通分包方案,小程序还有独立分包的方案。 独立分包是小程序中一种特殊类型的分包,能够独立于主包和其余分包运行。从独立分包中页面进入小程序时,不须要下载主包。当用户进入普通分包或主包内页面时,主包才会被下载。 咱们能够把它用于一些比较独立的页面,好比活动页等。
到了首屏渲染这个环节,有如下的几个优化建议:
提早请求:在页面 onLoad
阶段就能够发起异步请求,不用等页面 ready
。若是能在前置页面点击跳转时预请求当前页的核心异步请求,效果会更好;
善用缓存:对一些变更频率很低的异步数据进行缓存,下次启动时能够直接利用;
优化交互:在首屏渲染的期间,利用 loading
效果或展现骨架图,来缓解用户等待的焦虑。
其实,页面初始化的时间大体由页面初始数据通讯时间和初始渲染时间两部分构成。其中,数据通讯的时间指数据从逻辑层开始组织数据到视图层彻底接收完毕的时间,数据量小于 64KB
时总时长能够控制在30ms内。传输时间与数据量大致上呈现正相关关系,传输过大的数据将使这一时间显著增长。于是减小传输数据量是下降数据传输时间的有效方式。
初始渲染完毕后,视图层能够在开发者调用setData后执行界面更新。
与传统的浏览器 Web
页面最大区别在于,小程序的是基于 双线程 模型: 在这种架构中,视图层使用 WebView
做为渲染载体,而逻辑层是由独立的 JavascriptCore
做为运行环境。
二者都是独立的模块,并不具有数据直接共享的通道。视图层和逻辑层的数据传输,要由 Native 的 JSBrigde
作中转。
小程序经过 setData
更新数据到视图改变,完整的流程以下:
setData
方法;JSON.stringify
来去除掉 setData
数据中不可传输的部分,将待传输数据转换成字符串并拼接到特定的JS脚本, 并经过 evaluateJavascript
执行脚本将数据传输到渲染层。WebView JS
线程会对脚本进行编译,获得待更新数据后进入渲染队列等待 WebView
线程空闲时进行页面渲染。WebView
线程开始执行渲染时,将 data
和 setData
数据套用在WXML
片断上,获得一个新节点树。通过新虚拟节点树与当前节点树的 diff
对比,将差别部分更新到UI视图。最后,将 setData
数据合并到 data
中,并用新节点树替换旧节点树,用于下一次重渲染。如上文所说:一次 setData
带来两次开销:通讯的开销 + WebView
更新的开销。 setData
是小程序开发使用最频繁的 API
之一,也是最容易引起性能问题的。
因此使用时须要注意如下几点:
与界面渲染无关的数据最好不要设置在 data
中,能够考虑设置在 page
对象的其余字段下;
this.setData({
a: '与渲染有关的字符串',
b: '与渲染无关的字符串'
})
// 能够优化为
this.setData({
a: '与渲染有关的字符串',
})
this.b = '与渲染无关的字符串'
复制代码
不要过于频繁调用 setData
,应考虑将屡次 setData
合并成一次 setData
调用;
this.setData({ a: 1 })
this.setData({ b: 2 })
// 可优化为
this.setData({ a: 1, b: 2 })
复制代码
当须要在频繁触发的用户事件(如 PageScroll 、 Resize 事件)中调用 setData ,合理的利用函数防抖(debounce) 和 函数节流(throttle) 。
还能够本身设计一个 diff
算法,从新对 setData 进行封装,使得在 setData
执行以前,让待更新的数据与原 data
数据作 diff
对比,若是同样则跳过执行更新。很多小程序框架都有其相似的封装。
列表局部更新 在更新列表的某一个数据时。不要用 setData
进行所有数据的刷新。查找对应 id
的那条数据的下标(index
是不会改变的),用 setData
进行局部刷新。
this.setData({
`list[${index}]` = newList[index]
})
复制代码
合理使用小程序组件 自定义组件的更新只在组件内部进行,不会影响页面其余元素。由于各个组件具备独立的逻辑空间、数据、样式环境及 setData
调用。 基于自定义组件的 Shadow DOM
模型设计,咱们能够将页面中一些须要高频执行 setData
更新的功能模块(如倒计时、进度条等)封装成自定义组件嵌入到页面中。 当这些自定义组件视图须要更新时,执行的是组件本身的 setData
,新旧节点树的对比计算和渲染树的更新都只限于组件内有限的节点数量,有效下降渲染时间开销。
固然,并非使用自定义组件越多会越好,页面每新增一个自定义组件, Exparser
须要多管理一个组件实例,内存消耗会更大。所以要合理的使用自定义组件,同时页面设计也要注意不滥用标签。
其实性能优化最重要的是拿数听说话。可是如今小程序里没有完整成熟的定量的性能评测标准,目前有如下的分析工具可供参考:
性能 Trace 工具 微信 Andoid 6.5.10
开始,提供了 Trace
导出工具,开发者能够在开发者工具 Trace Panel
中使用该功能。 使用教程: developers.weixin.qq.com/miniprogram…
性能面板 从微信 6.5.8
开始,提供了性能面板让开发者了解小程序的性能。开发者能够在开发版小程序下打开性能面板。 打开方法:进入开发版小程序,进入右上角更多按钮,点击 显示性能窗口
加载性能监控 在小程序后台,咱们能够看到加载性能监控。指标有三个:
启动总耗时
下载耗时
初次渲染耗时
总启动耗时 = 下载耗时 + 初次渲染耗时 + 其余耗时。
优化后可根据以上几个工具进行数据对比,来判断优化的效果。
主要参考来源: