🔥想不到吧!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指令

相关推荐
却尘13 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare13 小时前
浅浅看一下设计模式
前端
Lee川13 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix14 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人14 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl14 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人14 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼14 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空14 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust