uni-app 性能优化建议

1. 前言

初期学习开发 uni-app 项目,我们一般不会在意项目的性能问题,项目功能较少或者数据较少的时候,不会对项目性能的重要性有很大的感受。

但如果开发大型项目,或者数据量比较大的时候,项目性能的问题就显得尤为重要。一种需求的不同实现方式,页面的加载速度会差几倍甚至上百倍。

所以我们在开发项目之前,最好先了解一下项目性能优化的几种方式,在项目初期就打好基础,以免后期维护过于麻烦。

这节课我们主要讲解一些性能优化的小建议,在项目开发之前最好看一看,知道开发的重点需要注意哪些地方。

2. uni-app 运行原理

uni-app 项目的视图层和逻辑层是分离开的,虽然我们在开发项目过程中,将 html、js 代码都写在同一个文件中,但是实际运行的时候是分离开的。

视图层负责进行页面渲染,也就是用户能看到的页面,用来展示数据的,包括页面结构代码 <template> 部分、页面样式代码 <style> 部分。

逻辑层负责执行后端的业务逻辑,用户看不到这部分的逻辑,用来处理数据的,包括页面逻辑代码 <script> 部分,以及其他 .js 文件。

视图层和逻辑层分离,减少了项目的耦合程度,并且逻辑层的运算不会影响到视图层的渲染,窗体动画会比较稳。而两层分离也有一个缺点,就是这两层互相通信会有损耗。

我们下面进行的性能优化,都是基于uni-app 运行原理来操作的,下面来具体看一下。

3. 性能优化建议

3.1 长列表优化

我们开发项目时,我们经常会循环长列表,将长列表中的数据逐一展示在项目中,但是你开发过程中,有没有出现过一旦数据过多项目加载会变得很慢的问题呢?

长列表的应用有许多需要我们注意的地方,或许你在开发过程中没有注意下面几个问题造成长列表加载过慢,我们来具体看看。

3.1.1 长列表差量数据更新

如果你长列表的数据中,每个列表都有可能差量更新,则需要将长列表中的每个 item 都做成一个组件。不然其中每个 item 更新,都会造成整个长列表的重新加载,严重良妃系统资源,我们来举个例子。

比如我们加载博文的100条评论,每条评论都有一个点赞功能。
如果每条评论没有做成单独的组件,用户每次给其中一条评论点赞一次,系统都会重新加载这100条评论。

如果每条评论都做成了单独的组件,用户给其中一条评论点赞,系统只会重新加载点赞的这一条评论,其他评论不受影响,合理利用系统资源。

实例:

<template>
	<view>
		<view class="thumb" v-for="item in testdata">
			<view>{{item}}</view>
		</view>
	</view>
</template>

<script>
	export default {	
		data() {
			return {
				testdata: ["评价1","评价2","评价3","评价4","评价5"]
			}
		}
	}
</script>

<style>
	.thumb{
		text-align: center;
		margin-top: 20px;
	}
</style>

// thumbitem.vue 将每个 item 包装成组件
<template>
	<view>
		<!-- 显示 item 信息 -->
		<view>{{item}}</view>
		<!-- 点赞按钮,点击触发 thumb 方法 -->
		<button @click="thumb(index)">点赞数:{{thumbs}}</button>
	</view>
</template>

<script>
	export default {
		props:['item'],
		data() {
			return {
				thumbs:0
			};
		},
		methods: {
			// 每次触发 thumb 方法,点赞数 thumbs 变量就加 1
			thumb(){
				this.thumbs += 1
			}	
		}
	}
</script>

3.1.2 长列表无差量数据更新

如果长列表中,每个 item 不会单独去更新,那我们就没有必要去将每个 item 都做成一个组件了,直接循环长列表显示 item 就可以。
实例:

// index.vue 循环加载长列表
<template>
	<view>
		<view class="thumb" v-for="item in testdata">
			<ThumbItem :item='item'></ThumbItem>	
		</view>
	</view>
</template>

<script>
	Import ThumbItem from "components/thumbitem/thumbitem.vue"
	export default {	
		components: {
			ThumbItem
		},
		data() {
			return {
				testdata: ["评价1","评价2","评价3","评价4","评价5"]
			}
		}
	}
</script>

<style>
	.thumb{
		text-align: center;
		margin-top: 20px;
	}
</style>

我们在开发过程中,不需要每次都将长列表的 item 包装成组件,每个 item 需要差量数据更新的时候,才需要包装成组件。

组件在页面初始化时会占用更多的内存,并且遍历节点也会更慢,每个组件渲染时都会触发一次通信,太多组件就会阻塞通信。所以我们要将好刀用在刀刃上,不分情况到处使用反而会适得其反。深层节点的嵌套也是同样的道理,我们开发的时候要注意尽量避免深层节点嵌套。

在实际项目开发中,长列表一般是由逻辑层处理后返回的,数据是变化的,如果长列表中的数据需要展示在页面上,那么我们就将长列表定义在 data 中,如果变量不需要展示在视图中,我们尽量将变量定于在 data 外部。

因为data 中的数据每次发生变化,视图层都要重新渲染页面。这样做可以尽量避免资源的浪费,这条建议同样也适用于其他变量。

3.2 减少一次性加载的节点数量

页面初始化时,逻辑层如果一次性向视图层传递很多数据,不仅会造成逻辑层和视图层之间的通讯变化,由于视图层一次渲染的数据过多,还会造成页面的卡顿。

所以如果数据过多,建议进行分页加载。比如:服务端要返回1000条数据,可以一次加载100条,500ms 后再进行下一次加载。

3.3 尽量避免两层频繁通信

上面我们提到过,逻辑层与视图层分离会造成一个缺点,就是两层之间通讯的损耗,如果两层之间频繁通信,可能会造成通讯阻塞,影响项目的正常运行,如果尽量避免两层之间的频繁通信呢?主要有以下几点:

3.3.1 scroll-view 组件的使用

  • 使用scroll-view 组件时,要尽量减少监听滚动事件,因为在监听 scroll-view 的滚动事件时,视图层会频繁的向逻辑层发送数据;
  • 不要实时的改变 scroll-top、scroll-left 属性,可能造成通讯卡顿;
  • 如果要实现下拉刷新的功能,建议使用页面的滚动,而不是 scroll-view;
  • 不要在 scroll-view 组件中放长列表,会导致性能问题。

3.3.2 图片、动画加载问题

  • 尽量使用压缩图片;
  • 尽量使用css 动画,而不是通过 js 的定时器操作界面做动画;
  • 如果项目中不可避免的需要加载大量的动画和图片,建议加载时进行延时处理,分批进行大量数据通讯,提高页面的流畅性。否则会造成页面切换卡顿、掉帧等问题;
  • App-nvue 和 H5 项目总,还支持页面预载,使用API uni.preloadPage,开发时可以使用提高用户的操作体验。

3.4 使用nvue代替vue

因为 nvue 页面开发限制比较多,我们前面建议大家尽量不要使用 nvue 来开发项目,但是如果更加看重项目性能问题,可以使用nvue 来代替 vue。

因为 nvue 是基于 weex 的原生渲染,可以有效提高页面的流畅程度。
如果对项目启动速度有更高的要求,如果项目支持的话,甚至可以将项目设置为纯 nvue 项目,这样整个应用都使用原生渲染,不加载 webview 框架,这样项目的启动速度会更快。

4. 小结

性能优化问题,在项目开发过程中,会变得越来越重要,最好在开发之前就了解各种优化性能的小技巧,这样后期开发也会更加得心应手一些。

本小节可以先浏览一遍项目开发中性能优化的这几个建议,大体记得哪些方面需要注意。等项目用到的时候,再来结合课程应用到实际项目中,这样也会理解的更加深刻。