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

相关推荐
待磨的钝刨35 分钟前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
逐·風4 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫4 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦5 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子5 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山6 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享6 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果6 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄7 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰7 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式