【问题标题】:Vue updating computed object, or updating the original?Vue 更新计算对象,还是更新原始对象?
【发布时间】:2023-01-04 23:27:14
【问题描述】:

我正在使用 computed() 方法将一些数据添加到对象的 ref() 数组中。

使用这个计算的对象数组可以读取数据,例如使用 v-for,但是当我尝试更新数据时它被忽略(没有任何反应),下面的示例显示了工作代码与不工作代码。

在 useCart 可组合项(参见下面的代码)中,我创建了一个 computedCartItems,它映射购物车并为每个项目添加总价。现在在我的 index.vue 文件中,我尝试增加 cartItem 的数量,如果我使用 <div v-for="cartItem in cart"> 遍历 cart,这会起作用,但在使用计算对象 <div v-for="cartItem in computedCartItems"> 时它会被忽略

使用Cart.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(工作,使用原始的“购物车”对象)

<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>

【问题讨论】:

  • 计算用于创建派生数据。您不能修改基本计算(您可以使用 getter 和 setter 创建计算)。您必须修改源数据。我可以尝试将其写成一个正确的答案 - 基于文档 - 如果你愿意的话?

标签: javascript vue.js vuejs3


【解决方案1】:

TLDR;您正在更新原始对象副本的值。它们没有链接,因此原始对象不会收到更新后的值。


详细解答

计算是只读.它们是派生数据,不应更新。
因为这是 javascript,你可以通过引用更新对象属性,但你真的不应该这样做,这是一种导致不明确副作用的糟糕做法。

查看计算的打字稿类型:

export declare interface ComputedRef<T = any> extends WritableComputedRef<T> {
    readonly value: T;
    [ComputedRefSymbol]: true;
}

所以 myComputed.value 是只读的,不能被赋予另一个值。您仍然可以执行 myComputed.value.myProperty = 'foo',但如前所述,这是一种不好的做法。

更多信息请访问official documentation

一个可能的解决方案

为每个项目(而不是整个购物车)创建 totalPrice 可组合项,并在项目对象内分配计算值。

const useItem = (reactiveItem) => {
  const totalPrice = computed(() => reactiveItem.amount * reactiveItem.price)
  
  // Assign a new property in your item, which is the derived totalPrice
  reactiveItem.totalPrice = totalPrice

  return reactiveItem
}

const useCart = () => {
  const cart = ref([])

  // Export a custom function to include the item and make it reactive + use composable (saves the final client from doing it)
  const addItem = (item) => {
    cart.value.push(useItem(reactive(item)))
  }

  return { cart, addItem }
}

const { cart, addItem } = useCart()

function createItem() {
  addItem({ amount: 5, price: 10 })
}

用一个工作示例检查这个online playground

我敢肯定还有其他方法可以做到这一点,这只是一种。例如,您可以使用 watch 来响应您的购物车更改。

【讨论】:

    猜你喜欢
    • 2017-09-01
    • 2021-12-17
    • 2018-02-08
    • 1970-01-01
    • 2019-08-10
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 2017-11-08
    相关资源
    最近更新 更多