🔥想不到吧!v-for使用不当会出现数据重载导致性能问题

前言

大家好,对于vue项目来说,v-for那真是随处可见,不知道大家平时有没有注意到,在使用v-for时导致的整个列表计算重载产生的性能问题。我自己倒还真没注意过,对于一些列表数据比较少的可能影响较小,但对于一些长列表存在比较复杂的计算的话,还是会存在较大的性能消耗。下文将通过一个例子说明问题,并据此提出了一些解决方案。

背景

最近一个朋友向我发出的这个问题,问我有没有遇到过这个问题。具体的例子 就是在做购物车 时,当增加购物车中的某一个商品的购买数量 时,会触发整个购物车列表的重载,导致购物车中的所有商品的价格都重新计算了一遍。想要解决的问题是:"能不能只触发修改的那个商品的计算,其他商品跳过更新。"

v-for触发整个列表数据重载问题示例

下面我们就用这个简单的购物车案例,说明一下这个问题,页面及代码如下:

order.vue

js 复制代码
<template>
  <div class="app">
    <div class="cart">
      <div class="item" v-for="item in list" :key="item.id">
        <span>名称:{{ item.name }}</span>------
        <span>购买数量:{{ item.amount }}</span>------
        <span>价格:{{ item.price }}</span>------
        <span>小计:{{ subPrice(item.price, item.amount) }}</span>
      </div>
    </div>
    <button @click="change">双倍快乐</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 购物车清单
const list = ref([
  { id: 'a', name: '可乐', amount: 1, price: 2 },
  { id: 'b', name: '炸鸡', amount: 2, price: 3 },
  { id: 'c', name: '汉堡', amount: 3, price: 4 },
  { id: 'd', name: '薯条', amount: 4, price: 5 }
])
// 计算小计
const subPrice = (price, amount) => {
  console.log(price, amount)
  return price * amount
}
// 可乐数量+1
const change = () => {
  list.value[0].amount++
}
</script>

当我们点击双倍快乐按钮 时,使可乐数量+1,我们期望的效果是只更新可乐节点的小计 ,其他节点则不需要触发subPrice()小计计算方法更新视图。但是,很遗憾,点击按钮后,你会发现控制台输出把所有商品的小计都重新计算了一遍!

下面我们说下解决办法!

封装Item组件优化

我们把单个商品元素封装成一个组件引用,使用v-for来渲染Item组件

Item.vue

js 复制代码
<template>
  <div class="item">
    <span>名称:{{ data.name }}</span>------
    <span>购买数量:{{ data.amount }}</span>------
    <span>价格:{{ data.price }}</span>------
    <span>小计:{{ subPrice(data.price, data.amount) }}</span>
  </div>
</template>

<script setup>
defineProps(['data'])
const subPrice = (price, amount) => {
  console.log(price, amount)
  return price * amount
}
</script>

order.vue

js 复制代码
<template>
  <div class="app">
    <div class="cart">
      <Item v-for="item in list" :key="item.id" :data="item"></Item>
    </div>
    <button @click="change">双倍快乐</button>
  </div>
</template>
...省略以下前面写到的代码

这时,我们再点击按钮使可乐+1 时,由上图可看出,只触发了可乐 对应的Item组件下的小计方法。

由此我们也可得出一个结论,在一些使用v-for渲染的列表中,视图依赖data触发更新的例子(比如:购物车、点赞、收藏 ),封装成一个Item组件还是很有必要的,单独引用的组件,才能做到差量数据更新 ,否则会造成整个列表数据重载。试想一下,如果渲染一个十万条数据的列表,修改其中一项导致整个列表重载,其中将会导致巨大的性能消耗。

v-memo优化

除了封装Item组件外,在vue.js@3.2+版本中,其实也新出了一个内置指令v-memo,可以优化以上问题,达到和封装Item组件那样的效果,这里我从vue官网截了个图简单介绍一下,想要了解更多关于v-memo的介绍,可自行查阅官网。废话不多说,我们下面使用v-memo指令解决以上问题。

其实用法很简单,只需在v-for的元素上增加v-memo="[item.amount]",当数组里面的item.amount改变时,只会触发对应item的视图更新,没有改变的item则会跳过更新。

js 复制代码
<template>
  <div class="app">
    <div class="cart">
      <div class="item" v-for="item in list" :key="item.id" v-memo="[item.amount]">
        <span>名称:{{ item.name }}</span>------
        <span>购买数量:{{ item.amount }}</span>------
        <span>价格:{{ item.price }}</span>------
        <span>小计:{{ subPrice(item.price, item.amount) }}</span>
      </div>
    </div>
    <button @click="change">双倍快乐</button>
  </div>
</template>

我们点击双倍快乐 按钮,打印如下图所示,也实现了差量更视图的效果。

总结

vue项目开发,使用v-for进行列表渲染时,可根据业务需求看是否需要操作列表数组更新视图(比如:增加商品数量、点赞、收藏等)确定是否需要封装组件单独引用,如有相关操作,从性能优化的角度看,建议封装成一个Item组件,单独引入使用,以实现差量数据更新 的目的。如果渲染列表数据量比较大,且项目使用的是vue3.2+版本,也可考虑使用v-memo的优化手段来解决整个列表数据重载问题

参考文献

uniapp官网-优化长列表数据更新

vue官方文档-v-memo指令

相关推荐
虾球xz8 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇13 分钟前
HTML常用表格与标签
前端·html
疯狂的沙粒17 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员33 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐35 分钟前
前端图像处理(一)
前端
程序猿阿伟42 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒44 分钟前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M1 小时前
node.js第三方Express 框架
前端·javascript·node.js·express