我使用computed()方法向ref()对象数组添加一些数据。
使用这个对象的计算数组可以阅读数据,例如使用v-for,但是当我试图更新数据时,它被忽略了(什么也没发生),下面的例子显示了工作代码和不工作代码。
在useCart可组合对象(见下面的代码)中,我创建了一个computedCartItems
,它Map购物车并为每个商品添加totalPrice。现在,在index.vue文件中,我尝试增加cartItem的金额,如果使用<div v-for="cartItem in cart">
循环cart
,这将起作用,但使用计算对象<div v-for="cartItem in computedCartItems">
时,它将被忽略
使用购物车.js
const useCart = () => {
const cart = ref([])
const computedCartItems = computed(() => {
return cart.value.map(cartItem => {
return {
...cartItem,
totalPrice: cartItem.amount * cartItem.price
}
})
})
return {
cart,
computedCartItems,
}
}
export default useCart
index.vue(不工作,使用计算的“computedCartItems”对象)
<div v-for="cartItem in computedCartItems">
<div>
<div>{{ cartItem.name }}</div>
<button @click="onIncrement(cartItem)">+</button>
</div>
</div>
<script setup>
const { cart, computedCartItems } = useCart()
const onIncrement = ({ id }) => {
const shoppingCartItemIndex = computedCartItems.value.findIndex(item => item.id === id)
computedCartItems.value[shoppingCartItemIndex].amount++
}
</script>
index.vue(工作,使用原始“cart”对象)
<div v-for="cartItem in cart">
<div>
<div>{{ cartItem.name }}</div>
<button @click="onIncrement(cartItem)">+</button>
</div>
</div>
<script setup>
const { cart, computedCartItems } = useCart()
const onIncrement = ({ id }) => {
const shoppingCartItemIndex = cart.value.findIndex(item => item.id === id)
cart.value[shoppingCartItemIndex].amount++
}
</script>
2条答案
按热度按时间lsmepo6l1#
您正在更新原始对象副本上的值。它们没有链接,因此原始对象不会收到更新后的值。
详细回答
Computed为只读。它们是派生数据,不应更新。
因为这是javascript,你可以通过引用来更新对象属性,但是你真的不应该这样做,这是一个不好的做法,会导致不清楚的副作用。
请参阅计算的 typescript 类型:
所以
myComputed.value
是只读的,不能赋值给其他值,你仍然可以赋值给myComputed.value.myProperty = 'foo'
,但是,正如前面提到的,这是一个不好的做法。有关此方面的更多信息,请参阅正式文件
一个可能的解决方案
为每个项目(而不是整个购物车)创建可组合的totalPrice,并在您的item对象中分配计算的。
用一个工作示例检查这个在线Playground。
我相信还有其他的方法可以做到这一点,这只是其中之一。例如,你可以使用
watch
来响应购物车的变化。li9yvcax2#
核心问题
计算参考是衍生数据:它以某种方式代表您的数据;不是直接更新它,而是更新它的源。
在文档中有一个关于这个问题的章节非常简洁地解释了这个问题:
避免改变计算值
从计算属性返回的值是派生状态。请将其视为临时快照-每次源状态更改时,都会创建一个新快照。更改快照没有意义,因此应将计算返回值视为只读且永远不会更改-而是更新它所依赖的源状态以触发新计算。
在您的非工作示例中,您没有尝试更新实际计算的ref(这甚至是不可能的;参见答案末尾的参考文档);您正在更新ref值的 properties,您 * 可以 * --但不应该--这样做。然而,除了所有其他问题之外,计算值不会更新,因为总价基于
cart
中的原始项目,而不是计算值中的项目,这意味着永远不会触发更新(因为cart
没有更改)。如果改为修改源ref(
cart
),则计算的ref将更新,示例将正常工作:一种(可能)更好的方式
虽然这样做是可行的,但它很可能不是解决特定问题的理想方法,因为每次更新一个项时,整个计算数组和其中的每个项都将重新创建,这是非常低效的。
相反,您可以让
useCart
组合只返回单个cart ref沿着一些操作cart的方法。现在cart的处理是由
useCart
可组合组件完成的,通过抽象内部组件使消费者的工作更容易。除了上面提到的好处,这还意味着可组合组件仍然可以控制其数据,因为cart引用不能被修改。“关注点分离”等。文档参考等
Vue文件
Computed Properties - Vue.js Docs
计算引用的要点在于它们会根据其来源自动更新。你不需要直接修改它们,你需要修改它们的来源。
computed属性自动跟踪其React依赖项。Vue知道
publishedBooksMessage
的计算依赖于author.books
,因此当author.books
更改时,它将更新依赖于publishedBooksMessage
的任何绑定。不能将值赋给常规计算引用。
默认情况下,计算属性是仅getter属性。如果尝试为计算属性分配新值,将收到运行时警告。在极少数情况下,如果需要“可写”计算属性,可以通过同时提供getter和setter来创建计算属性。
我强烈推荐阅读"Reactivity Fundamentals" section of the Vue Guide,特别是参见“ReferUnwrappinginReactiveObjects”,了解如何在
reactive
中嵌套计算的ref。我还建议你准备好的时候,浏览一下整个"Reactivity in Depth" page,它会让你了解React系统实际上是如何工作的。
其他链接
VueUse是一个伟大的资源,无论是对许多得心应手的可组合和学习。