前言
下有一段Vue3代码,用于根据单价和数量计算单品总价
<template>
<div>
<h1>商品列表</h1>
<div
v-for="(item, index) in items"
:key="index"
style="margin-bottom: 20px"
>
<div>
商品名称:{{ item.name }} <br />
单价:¥{{ item.price }} <br />
数量:
<input type="number" v-model="item.count" min="0" />
<br />
总价:¥{{ totalPrice(item) }}
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue"
const items = ref([
{ name: "商品1", price: 100, count: 1 },
{ name: "商品2", price: 200, count: 1 },
{ name: "商品3", price: 300, count: 1 },
])
const totalPrice = (item) => {
console.log(item)
return item.price * item.count
}
</script>
<style scoped></style>
目前无法使用缓存,每次修改数量会连带其他商品一起重新计算,已知Vue3中的 computed
是有缓存的,但是 computed
无法接收参数,所以需要自己封装一个 类似于 React 中 useMemo
的Hook。
实现
封装 一个名为 useComputed 的 Hook 以实现缓存值功能,该 Hook 传入一个类型为函数的参数(本文中的totalPrice)并返回一个新函数,新函数与参数函数有相同的功能但是带有缓存。
初步结构如下:
import { computed } from "vue"
export function useComputed(fn) {
return function (...args) {
if (有缓存) {
return 缓存结果
}
const result = computed(() => fn(...args)) // args有可能是响应式数据,用computed处理
缓存结果 = result
return result
}
}
利用args参数,也就是每一个商品对象,建立对应关系
import { computed } from "vue"
export function useComputed(fn) {
const cache = new Map()
function compare(args1, args2) {
return (
args1.length === args2.length &&
args1.every((item, i) => Object.is(item, args2[i])) // Object.is两个NaN判断为相等,===则不会
)
}
function getCache(args) {
const keys = [...cache.keys()]
const key = keys.find((item) => compare(item, args))
if (key) {
return cache.get(key)
}
}
return function (...args) {
const cachedResult = getCache(args)
if (cachedResult) {
return cachedResult.value
}
const result = computed(() => fn(...args))
cache.set(args, result)
return result.value
}
}
<template>
<div>
<h1>商品列表</h1>
<div
v-for="(item, index) in items"
:key="index"
style="margin-bottom: 20px"
>
<div>
商品名称:{{ item.name }} <br />
单价:¥{{ item.price }} <br />
数量:
<input type="number" v-model="item.count" min="0" />
<br />
总价:¥{{ computedPrice(item) }}
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue"
import { useComputed } from "./useComputed"
const items = ref([
{ name: "商品1", price: 100, count: 1 },
{ name: "商品2", price: 200, count: 1 },
{ name: "商品3", price: 300, count: 1 },
])
const totalPrice = (item) => {
console.log(item)
return item.price * item.count
}
const computedPrice = useComputed(totalPrice)
</script>
<style scoped></style>
可以看到每次只针对操作的商品重新计算,其余商品使用缓存值