首页 前端知识 Vue如何给计算属性computed传参的思考,看完就明白了!

Vue如何给计算属性computed传参的思考,看完就明白了!

2024-09-06 00:09:43 前端知识 前端哥 66 260 我要收藏

一、缘起

事情的起因是这样的,有位朋友(无中生友)遇到了如下需求:

在这里插入图片描述

上面是一个商品列表,每个商品对应一个价格、优惠、数量,并且数量可以动态改变,最后动态计算出一个总价。当然,这只是一个简单地乘法计算,往往在实际项目开发中,遇到的需求要复杂的多,计算也更复杂。

大部分人第一时间想到是利用函数传参返回计算结果,显示在页面上。实现方式如下:

function getSumMoney(row) {
  return row.price * row.num * row.discount;
}

结果如下:
在这里插入图片描述

当改变数量时,总价也跟着变化,看似没有任何问题,达到了我们的预期结果。其实不然,我们看下面的gif演示就会发现问题。

在这里插入图片描述

观察上面的gif动图,细心地你已经发现问题了。观察浏览器的打印日志发现,每次改变一个商品的数量时,其它商品的数量虽然没有变化,但是也会在计算一次。还有,当浏览器视口宽度高度变化时,我们并没有改变商品的数量,同样的也会每次触发计算,这个方法会被不断反复的调用。

在这里插入图片描述

这是为什么呢?

这是因为当监听到表格的数据data变化时,会重新渲染列表;当浏览器视口宽度高度变化时,会引发重排,列表也会重新渲染。

当计算量大且复杂时,这种情况是相当耗性能的,并且体验也不好。我们期望的结果是哪一行变化时重新计算哪一行,数据不变的行不需要重新计算。

二、computed如何传参?

针对以上情况,大家第一时间可能想到是利用计算属性computed传入一个参数。计算属性computed是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。

那么问题来了,Vue中计算属性computed如何传参?

答:计算属性是不能直接传参的。在vue3中的组合式API中,computed是无法传入参数的。在vue2的选项式API中,computed可以通过闭包函数(也叫匿名函数)间接传参来实现。

代码如下:

<el-table-column prop="sum" label="总价">
    <template #default="scope">
        {{ computedPrice(scope.row) }}
    </template>
</el-table-column>
computed: {
    computedPrice() {
        return function(row) {
            console.log(row)
            return row.price * row.num * row.discount;
        }
    }
}

在这里插入图片描述

很明显以上方法也没有实现我们想要的结果!那么到底怎么才能实现我们的预期结果呢?

三、分析与实现

我们仔细分析一下,其实这个需求就是希望为每一个数据参数对应一个计算属性computed,当每一行数据不变时,就返回这个没有改变的计算属性。反之某一行数据变化了,那么它对应的计算属性也发生了变化。也就是说,一个参数对应一个计算属性,每一行数据里面都有一个计算属性方法。

例如:

const tableData = ref([
  {
    product: '华为 Mate 60Pro',
    price: 8000,
    num: 1,
    discount: 0.9,
    totalPrice: computed(() => {
        // do something
    })
  },
  {
    product: '华为 Pura 70Pro',
    price: 7000,
    num: 1,
    discount: 0.95,
    totalPrice: computed(() => {
        // do something
    })
  },
  {
    product: '华为 Mate X5',
    price: 11500,
    num: 1,
    discount: 0.88,
    totalPrice: computed(() => {
        // do something
    })
  }
])

在这里插入图片描述

如果要这么实现非常复杂笨拙,而且当列表有非常多数据时现实起来也不切实际。那么我们可以封装一个函数useComputed,把我们真正要计算的函数传入useComputed并返回一个新的函数computedPrice,只需要用computedPrice去进行总价的计算即可。

完整代码如下:

<template>
    <div class="container">
        <el-table :data="tableData" border style="width: 100%">
          <el-table-column prop="product" label="产品" />
          <el-table-column prop="price" label="价格" />
          <el-table-column prop="discount" label="优惠" />
          <el-table-column prop="num" label="数量">
              <template #default="scope">
                  <el-input-number v-model="scope.row.num" :min="1" :max="10" :key="scope.row.price" />
              </template>
          </el-table-column>
          <el-table-column prop="sum" label="总价">
              <template #default="scope">
                  {{ computedPrice(scope.row) }}
              </template>
          </el-table-column>
        </el-table>
    </div>
</template>

<script setup>
  import { ref, computed } from 'vue';

  const tableData = ref([
    {
      product: '华为 Mate 60Pro',
      price: 8000,
      num: 1,
      discount: 0.9,
    },
    {
      product: '华为 Pura 70Pro',
      price: 7000,
      num: 1,
      discount: 0.95,
    },
    {
      product: '华为 Mate X5',
      price: 11500,
      num: 1,
      discount: 0.88,
    },
  ])


  function useComputed(fn) {
      const map = new Map();
      return function(...args) {
          const key = JSON.stringify(args);
          if(map.has(key)) {
              return map.get(key)
          }
          const result = computed(() => {
              return fn(...args)
          })
          map.set(key, result);
          return result;
      }
  }

  function totalPrice(row) {
      console.log(row)
      return row.price * row.num * row.discount;
  }

  const computedPrice = useComputed(totalPrice)
</script>

效果如下:

在这里插入图片描述

上图可以看到,哪一行数据变化了就重新计算哪一行,数据不变的行不进行计算,完美的达到了我们想要的结果。

下面就对重要代码进行分析:

function useComputed(fn) {
    // 创建Map缓存结果
    const map = new Map();
    // 返回一个函数
    return function(...args) {
        // args是一个数组,把参数转成字符串,当做Map的key
        const key = JSON.stringify(args);
        console.log(key); 
        // [{"product":"华为 Mate 60Pro","price":8000,"num":1,"discount":0.9}]把这一长串当做key
        // 判断是否有对应的计算属性
        if(map.has(key)) {
            // 有就返回计算属性
            return map.get(key)
        }
        // 没有就创建一个计算属性
        const result = computed(() => {
            return fn(...args)
        })
        // 把创建的计算属性返回的结果result和参数关联
        map.set(key, result);
        return result;
    }
}

在这里插入图片描述

转载请注明出处或者链接地址:https://www.qianduange.cn//article/17748.html
标签
评论
发布的文章

jquery video视频轮播播放

2024-09-27 09:09:58

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!