vue海量数据列表操作的性能优化,渣渣手机性能一秒变丝滑!

13,858 阅读4分钟

前言:

我们在做移动端的点餐程序的时候,发现当菜品数量巨大的时候,特别是外加很多操作的时候(比如菜品半整份切换的时候),列表里面的菜品数量 进行快速 加减和半整份切换就会卡顿。

那到底是什么让手机如此卡顿呢?

我写了个用例,用了2277条数据(找不到性能差的手机,可以适当增加数据量来测试)在 红米3安卓版本:5.1.1 LMY47V)上做了个实验。

Normal.vue为普通暴力渲染模式,Optimize.vue优化后的模式;

demo 地址:github.com/trsoliu/vue…

1.发生的原因:

当我们拿一个数组list用v-for直接把数组里的对象渲染到页面上的时候,其实,已经为后续的性能埋下了雷。

Normal.vue

<template>
	<div class="normal">
		普通列表
		<ul>
			<li v-for="(item,index) in list">
				<img :src="item.img" />
				<div class="left_bottom" >
					<p>{{item.name}}</p>
					<p><span v-if="item.num > 0">{{item.num}}整份</span><span v-if="item.num > 0">{{item.num}}半份</span></p>
					<span class="add btn" @click="add(index)">+</span>
					<span class="num">{{item.num}}</span>
					<span class="reduce btn" @click="reduce(index)">-</span>
				</div>
			</li>
		</ul>
	</div>
</template>

<script>
	import list from "./list.js"
	export default {
		name: 'normal',
		data() {
			return {
				list: list
			}
		},
		methods: {
			add(index) {
				let t = this;
				console.log(index, "+");
				t.list[index].num++;
			},
			reduce(index) {
				let t = this;
				t.list[index].num > 0 ? t.list[index].num-- : 0;
			}
		}
	}
</script>


在这里,每次触发➕或者➖来操作菜品数据时,都需要操作原list数组。

每次操作完原数组后,原数组list发生变化,会重新走一遍v-for进行页面重绘渲染(即使官方有跟踪每个节点的标识key,做重用和重新排序现有元素),那其实这个过程在少量数据的时候没什么影响,vue的速度很快。

但当用户的手机是那种300-1000块的街机或者系统老旧的时候,这里的性能问题就尤为的明显了;你会发现快速操作菜品加减的时候,菜品数字变化会缓慢或者直接跳值(比如直接从2变成5),同时你滑动页面的时候页面滚动也是卡顿的,因为这个时候页面在渲染,滚动条也会短时间停滞;

海量数据下,这种用户体验十分的糟糕,并且为我发现市场大部分的列表操作都有这类的问题。

2.解决办法

写一个单独的组件,将数组list中的每一个对象传给子组件(Operate.vue),组件用props接受到后,之后只操作传过来的这个对象,不改变原数组list中的任何值;

备注:这里为将一个数组变为两个,一个为 listOriginal 仅做初次渲染使用,另外一个listOperate 在组件回调后操作,用来做渲染之外的业务处理。这样页面渲染和业务操作通过子组件达到互补干扰的效果,页面也会极其流畅了。

以下红框为单独组件渲染结果:

父组件:Optimize.vue

<template>
	<div class="optimize">
		优化列表
		<ul>
			<Operate v-for="(item,index) in listOriginal" :item="item" :index="index" :key="index"></Operate>
		</ul>
	</div>
</template>

<script>
	import list from "./list.js"
	export default {
		name: 'optimize',
		data() {
			return {
				listOriginal: JSON.parse(JSON.stringify(list)),//仅做初次渲染使用
				listOperate:JSON.parse(JSON.stringify(list))//组件回调后操作,用来做渲染之外的业务处理
			}
		},
		components:{
			Operate: function(resolve) {
				require(['@/components/Operate.vue'], resolve)
			}
		}
	}
</script>

子组件:Operate.vue

<template>
	<li class="operate">
		<img :src="item.img" />
		<div class="left_bottom">
			<p>{{item.name}}</p>
			<p><span v-if="item.num > 0">{{item.num}}整份</span><span v-if="item.num > 0">{{item.num}}半份</span></p>
			<span class="add btn" @click="add(index)">+</span>
			<span class="num">{{item.num}}</span>
			<span class="reduce btn" @click="reduce(index)">-</span>
		</div>
	</li>
</template>

<script>
	export default {
		name: 'operate',
		data() {
			return {}
		},
		props: ["item", "index"],
		methods: {
			add(index) {
				let t = this;
				t.item.num++;
                                //t.$emit(...);//回调操作父组件listOperate,或者处理其他业务均可
			},
			reduce(index) {
				let t = this;
				t.item.num > 0 ? t.item.num-- : 0;
                                //t.$emit(...);//回调操作父组件listOperate,或者处理其他业务均可
			}
		}
	}
</script>


通过改变数据的使用方式,和合理利用框架,达到性能的优化。

以上大概就是我写的用例的基本对比情况,如果疑问可以留言给我或者加群,我都会一一解答的。


有问题可以加群交流535798405;

demo 地址:github.com/trsoliu/vue…