前言
Uni-App
,从了解到开发,相信大家都会觉得Uni-App
性能不好,其实也这是非原生的弊病。React Native
、Flutter
等,非原生框架,性能上都会或多或少的折损。但各个框架,都会做出性能提升建议,所以开发者在开发前,多了解一下,后面维护升级等就会更方便一点,否则项目越来越大,后续开发就会越来越难。
现在我们就从uni-app
运行原理上,了解一下,在哪些方面存在性能折损问题?
运行原理
逻辑层和视图层分离,非H5端通信有折损
uni-app
在非H5端运行时,从架构上分为逻辑层和视图层两个部分。逻辑层负责储存数据和执行业务逻辑,视图层负责页面渲染。
页面加载时,联网和逻辑运算在逻辑层(Android是v8
,iOS是jscore
),然后会传递数据给视图层渲染。这种通信有损耗。同样,在视图层操作时,比如拖动页面,要实时传递事件给逻辑层接收,也是有损耗的。
App端渲染引擎可切换
在App端,nvue
页面的视图层是由原生引擎渲染的,vue
页面的视图层是os的 webview
渲染的。 uni-app
的 webview
渲染经过优化,性能也足够好。它比 nvue
弱的地方主要在于启动速度和可左右拖动的长列表。
app-vue和小程序的数据更新,分页面级和组件级
对于复杂页面,更新某个区域的数据时,需要把这个区域做成组件,这样更新数据时就只更新这个组件,否则会整个页面的数据更新,造成点击延迟卡顿。 这就是自定义组件编译模式的特点。
比如微博长列表页面,点击一个点赞图标,赞数要立即+1,此时这个点赞图标一定要做成组件。否则这个+1会引发页面级所有数据的更新。
app-nvue和h5不存在此问题。造成差异的原因是小程序目前只提供了组件差量更新的机制,不能自动计算所有页面差量。
优化建议
使用自定义组件模式
使用自定义组件模式,在 manifest
中配置自定义组件模式(HBuilderX1.9起新建项目默认即为自定义组件模式)。
在复杂页面中,页面中嵌套大量组件,如果是非自定义组件模式,更新一个组件会导致整个页面数据更新。而自定义组件模式则可以单独更新一个组件的数据。
在App端,除了上述好处,自定义组件模式还新增了一个独立的js引擎,加快启动速度、减少js阻塞。
之前的非自定义组件模式已经不再推荐,如果你的应用还是非自定义组模式,请尽快升级。
避免使用大图
页面中若大量使用大图资源,会造成页面切换的卡顿,导致系统内存升高,甚至白屏崩溃。
尤其是不要把多张大图缩小后显示在一个屏幕内,比如上传图片前选了数张几M体积的照片,然后缩小在一个屏幕中展示多张几M的大图,非常容易白屏崩溃。
推荐通过阿里云oss,来压缩图片处理。
优化数据更新
在 uni-app
中,定义在 data
里面的数据每次变化时都会通知视图层重新渲染页面。 所以如果不是视图所需要的变量,可以不定义在 data
中,可在外部定义变量或直接挂载在vue实例上,以避免造成资源浪费。
长列表
长列表中如果每个item
有一个点赞按钮,点击后点赞数字+1,此时点赞组件必须是一个单独引用的组件,才能做到差量数据更新。否则会造成整个列表数据重载。(要求自定义组件模式)
长列表中每个item
并不一定需要做成组件,取决于你的业务中是否需要差量更新某一行item的数据,如没有此类需求则不应该引入大量组件。(点击item后背景变色,属于css调整,没有更新data数据和渲染,不涉及这个问题)
app端nvue
的长列表应该使用list
组件,有自动的渲染资源回收机制。vue页面使用页面滚动的性能,好于使用scroll-view
的区域滚动。
如需要左右滑动的长列表,请参考“在HBuilderX新建uni-app
项目” 的 新闻模板,那是一个标杆实现。自己用swiper
和scroll-view
做很容易引发性能问题。
减少一次性渲染的节点数量
页面初始化时,逻辑层如果一次性向视图层传递很大的数据,使视图层一次性渲染大量节点,可能造成通讯变慢、页面切换卡顿,所以建议以局部更新页面的方式渲染页面。
如:服务端返回100条数据,可进行分批加载,一次加载50条,500ms 后进行下一次加载。
减少节点嵌套层级
深层嵌套的节点在页面初始化构建时往往需要更多的内存占用,并且在遍历节点时也会更慢些,所以建议减少深层的节点嵌套。
避免视图层和逻辑层频繁进行通讯
减少 scroll-view
组件的 scroll
事件监听,当监听 scroll-view
的滚动事件时,视图层会频繁的向逻辑层发送数据;
监听 scroll-view
组件的滚动事件时,不要实时的改变 scroll-top/scroll-left
属性,因为监听滚动时,视图层向逻辑层通讯,改变 scroll-top/scroll-left
时,逻辑层又向视图层通讯,这样就可能造成通讯卡顿。
注意 onPageScroll
的使用,onPageScroll
进行监听时,视图层会频繁的向逻辑层发送数据;
多使用css
动画,而不是通过js的定时器操作界面做动画
优化页面切换动画
页面初始化时若存在大量图片或原生组件渲染和大量数据通讯,会发生新页面渲染和窗体进入动画抢资源,造成页面切换卡顿、掉帧。建议延时100ms~300ms渲染图片或复杂原生组件,分批进行数据通讯,以减少一次性渲染的节点数量。
App端动画效果可以自定义。popin/popout
的双窗体联动挤压动画效果对资源的消耗更大,如果动画期间页面里在执行耗时的js,可能会造成动画掉帧。此时可以使用消耗资源更小的动画效果,比如slide-in-right/slide-out-right
。
优化样式渲染速度
如果页面背景是深色,在vue页面中可能会发生新窗体刚开始动画时是灰白色背景,动画结束时才变为深色背景,造成闪屏。这是因为webview的背景生效太慢的问题。此时需将样式写在 App.vue
里,可以加速页面样式渲染速度。App.vue
里面的样式是全局样式,每次新开页面会优先加载 App.vue
里面的样式,然后加载普通 vue
页面的样式。另外nvue
页面不存在此问题,也可以更改为nvue
页面。
使用 nvue 代替 vue
在 App
端 uni-app
的 nvue
页面可是基于 weex
定制的原生渲染引擎,实现了页面原生渲染能力、提高了页面流畅性。若对页面性能要求较高可以使用此方式开发。
优化App启动速度的注意
工程代码越多,包括背景图和本地字体文件越大,对App的启动速度有影响,应注意控制体积。组件引用的前景图不影响性能。
App端的 splash 关闭有白屏检测机制,如果首页一直白屏或首页本身就是一个空的中转页面,可能会造成 splash 10秒才关闭。
App端使用自定义组件模式时启动速度更快,首页为nvue页面时启动速度更快
App设置为纯nvue项目(manifest
里设置app-plus
下的renderer:"native"
),这种项目的启动速度更快,2秒即可完成启动。因为它整个应用都使用原生渲染,不加载基于webview的那套框架。
优化包体积
1、uni-app
发行到小程序时,自带引擎只有几十K,主要是一个定制过的vue.js核心库。如果使用了es6
转es5
、css对齐的功能,可能会增大代码体积,可以配置这些编译功能是否开启。
2、uni-app
的H5端,自带了vue.js
、vue-rooter
及部分es6 polyfill
库,这部分的体积gzip后只有92k,和web开发使用vue基本一致。而内置组件ui库(如picker、switch等)、小程序的对齐js api等,相当于一个完善的大型ui库。但大多数应用不会用到所有内置组件和API。由此uni-app
提供了摇树优化机制,未摇树优化前的uni-app
整体包体积约500k,服务器部署gzip后162k。开启摇树优化需在manifest
配置。
3、uni-app
的App端,因为自带了一个独立v8引擎和小程序框架,所以比HTML5Plus
或mui
等普通hybrid
的App引擎体积要大。Android基础引擎约15M。App还提供了扩展模块,比如地图、蓝牙等,打包时如不需要这些模块,可以裁剪掉,以缩小发行包体积。在 manifest.json-App
模块权限 里可以选择。
4、App端支持如果选择纯nvue项目(manifest
里设置app-plus
下的renderer:"native"
),包体积可以进一步减少2M左右。
5、uni-app
的App端默认包含arm32和x86两个cpu的支持so库。这会增大包体积。如果你在意体积控制,可以在manifest里去掉x86 cpu的支持(manifest可视化界面-App其他设置里选择cpu),这可以减少包体积到9M。但代价是不支持intel的cpu了。一般手机都是arm的,仅个别少见的Android pad使用x86 cpu。另外as的模拟器里如果选择x86时也无法运行这种apk。
总结
性能优化可以总结以下几点:
1、减少页面级渲染
2、能用CSS解决的,不要用JS
3、减少M级图片,在保证分辨率的同时,尽量压缩图片
4、减少包的体积,去掉不必要的图片,字体文件备份文件等
同时,跟小伙们们说再见了,本专栏完结,同时回顾一下本专栏内容。本专栏主要通过一个简单的“天气”项目,让大家快速上手uni-app,从项目搭建、接口调用、原生交互、组件封装使用等,基本完成了一整套开发。
再次谢谢,支持我的小伙伴,期待下一次专栏能给大家带来更好更优质的内容。谢谢大家。