🔥想不到吧!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组件外,在[email protected]+版本中,其实也新出了一个内置指令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指令

相关推荐
风之舞_yjf33 分钟前
Vue基础(8)_监视属性、深度监视、监视的简写形式
javascript·vue.js·ecmascript
湛海不过深蓝38 分钟前
【css】css统一设置变量
前端·css
DONSEE广东东信智能读卡器1 小时前
蓝牙身份证阅读器使用Uniapp调用二次开发demo
javascript·uni-app·蓝牙·身份证阅读器
Codingwiz_Joy1 小时前
Day28 -js开发01 -JS三个实例:文件上传 & 登录验证 & 购物商城 & ---逻辑漏洞复现 及 判断js的payload思路
开发语言·javascript·安全·安全性测试
程序员的世界你不懂1 小时前
tomcat6性能优化
前端·性能优化·firefox
爱吃巧克力的程序媛1 小时前
QML ProgressBar控件详解
前端
进取星辰1 小时前
21、魔法传送阵——React 19 文件上传优化
前端·react.js·前端框架
wqqqianqian1 小时前
国产linux系统(银河麒麟,统信uos)使用 PageOffice 在线打开Word文件,并用前端对话框实现填空填表
linux·前端·word·pageoffice
BillKu1 小时前
CSS实现图片垂直居中方法
前端·javascript·css
长袖格子衫2 小时前
第五节:对象与原型链:JavaScript 的“类”与“继承”
开发语言·javascript·原型模式