小程序性能优化指南
Taro 3 为了兼容 React、Vue 等 Web 开发框架,在运行时对浏览器环境进行模拟,实现了 BOM 和 DOM 等一系列 API。相比 Taro 1/2,Taro 3 是一款重运行时、轻编译时的框架,在性能方面会有一定损耗。因此 Taro 3 提供了一系列的性能优化手段,从而提升 Taro 3 应用的性能。
#
优化更新性能经验证明,此项优化能大大减少 Taro3 的更新卡顿问题,尤其是在低端机上。
Taro3 使用小程序的 template
进行渲染,一般情况下并不会使用原生自定义组件。这会导致一个问题,所有的 setData
更新都是由页面对象调用,如果我们的页面结构比较复杂,更新的性能就会下降。
层级过深时 setData
的数据结构:
针对这个问题,主要的思路是借用小程序的原生自定义组件,以达到局部更新的效果,从而提升更新性能。
期望的 setData
数据结构:
开发者有两种办法可以实现这个优化:
#
1. 全局配置项 baseLevel对于不支持模板递归的小程序(微信、QQ、京东小程序),在 DOM 层级达到一定数量后,Taro 会使用原生自定义组件协助递归。
简单理解就是 DOM 结构超过 N 层后,会使用原生自定义组件进行渲染。N 默认是 16 层,可以通过修改配置项 baseLevel 修改 N。
把 baseLevel
设置为 8
甚至 4
层,能非常有效地提升更新时的性能。但是设置是全局性的,会带来若干问题:
flex
布局在跨原生自定义组件时会失效,这是影响最大的一个问题。SelectorQuery.select
方法的跨自定义组件的后代选择器写法需要增加>>>
:.the-ancestor >>> .the-descendant
#
2. CustomWrapper 组件为了解决全局配置不灵活的问题,我们增加了一个基础组件 CustomWrapper
。它的作用是创建一个原生自定义组件,对后代节点的 setData
将由此自定义组件进行调用,达到局部更新的效果。
开发者可以使用它去包裹遇到更新性能问题的模块,提升更新时的性能。因为 CustomWrapper
组件需要手动使用,开发者能够清楚“这层使用了自定义组件,需要避免自定义组件的两个问题”。
- React
- Vue
#
优化初次渲染性能一般情况下不需要开启,请根据实际情况进行使用。
当初次渲染的数据量非常大时,可能会导致页面白屏一段时间。因此 Taro 提供了预渲染功能来解决此问题。
#
优化长列表性能针对长列表的场景,Taro 提供了 VirtualList 组件辅助开发者进行优化。
它只会渲染当前可视区域内的组件,非可视区域的组件将会在用户滚动到可视区域内后再渲染,从而减少实际渲染的组件、优化渲染性能。
#
跳转预加载Taro 1 / 2 中提供的 componentWillPreload 钩子在 Taro 3 中已废弃。
在小程序中,从调用 Taro.navigateTo
等路由跳转 API 后,到小程序页面触发 onLoad
会有一定延时,因此一些网络请求可以提前到发起跳转的前一刻去请求。
Taro 3 提供了 Taro.preload
API,可以把需要预加载的内容作为参数传入,然后在新页面加载后通过 Taro.getCurrentInstance().preloadData
获取到预加载的内容。
例子:
- React
- Vue
- React
- Vue
#
写法最佳实践对小程序的性能影响较大的有两个因素,分别是 setData
的数据量和单位时间 setData
函数的调用次数。
当遇到性能问题时,在项目中打印 setData
的数据将非常有利于帮助定位问题。开发者可以通过进入 Taro 项目的 dist/taro.js
文件,搜索定位 .setData
的调用位置,然后对数据进行打印。
在 Taro 中,会对 setData
做 batch 捆绑更新操作,因此更多时候只需要考虑 setData 的数据量大小问题。
以下是我们梳理的开发者需要注意的写法问题:
#
1. 删除楼层节点要谨慎处理假设有一种这样一种结构:
Taro3 目前对节点的删除处理是有缺陷的。当 isShowModal
由 true
变为 false
时,模态弹窗会从消失。此时 Modal
组件的兄弟节点都会被更新,setData
的数据是 Slider
+ Goods
组件的 DOM 节点信息。
一般情况下,影响不会太大,开发者无须由此产生心智负担。但倘若待删除节点的兄弟节点的 DOM 结构非常复杂,如一个个楼层组件,删除操作的副作用会导致 setData
数据量较大,从而影响性能。
#
解决办法:目前我们可以这样优化,隔离删除操作:
我们正在对删除节点的算法进行优化,完全规避这种不必要的 setData,于 v3.1 推出。
#
2. 基础组件的属性要保持引用React
假设基础组件(如 View
、Input
等)的属性值为非基本类型时,尽量保持对象的引用。
假设有以下写法:
每次渲染时,React 会对基础组件的属性做浅对比,这时发现 markers
的引用不同,就会去更新组件属性。最后导致 setData
次数增多、setData
数据量增大。
#
解决办法:可以通过 state
、闭包等手段保持对象的引用:
#
3. 基础组件不要挂载额外属性基础组件(如 View
、Input
等)如若设置了非标准的属性,目前这些额外属性会被一并进行 setData
,而实际上小程序并不会理会这些属性,所以 setData
的这部分数据是冗余的。
例如 Text
组件的标准属性有 selectable
、user-select
、space
、decode
四个,如果我们为它设置一个额外属性 something
,那么这个额外的属性也是会被 setData。