如何在Vue.js应用程序中共享“computed”方法

9

我有一个Vue.js应用程序,它会加载一系列项目,并将每个项目作为prop传递给Vue组件。

我发现通过使用mixins,我可以共享常见的组件属性,例如computedcreated等。

现在,我正在尝试对项目列表进行排序,但是想不出如何访问每个组件的计算属性来进行排序/过滤。 我该如何解决这个问题?

项目

[{
  price: 10,
  qty: 2
}, {
  price: 8,
  qty: 3
}]

Mixin - ./Cost.js

export default {
  computed: {
     cost () {
        return this.price * this.qty;
     }
  }
}

组件(正常工作)- ./Product.vue

import Cost from './Cost.js'
export default {
   name: 'product-item',
   props: ['product'],
   mixins: [Cost]
}

你如何访问计算属性,或重新构建此设置?
列表组件
<template>
  <div id="list">
     <div v-for="product in sorted" :product="product">Cost: {{ cost }} </div>
  </div>
</template>

<script>
import ProductItem from './Product.vue'
export default {
   components: { ProductItem },
   created: () {
      this.items = [...] // as noted above
   },
   computed: {
       sorted () {
           return this.items.sort( (a,b) => b.cost - a.cost); // cost is not accessible!
       }
   }
}
</script>

你对 this.items 进行了排序,但是 this.items 是一个包含两个对象的数组,每个对象都有 priceqty 属性,而不是每个元素都是 ProductItem 组件。显然它们没有 cost 属性。 - Sphinx
是的,我知道...正在寻找访问它们计算属性的想法。 - d-_-b
1
一种解决方案是使用属性=ref,例如<div v-for="product in sorted" :product="product" ref="test">,然后 computed: () { return this.$refs.test.sort( (a,b) => b.cost - a.cost); // cost is not accessible! } - Sphinx
2
computed 不应该返回一个带有方法的对象吗? computed: { sorted() { return this.items.sort( (a,b) => b.cost - a.cost); }} - Wildan Maulana Syahidillah
个人而言,我认为在返回结果中应该有一个名为“cost”的排序字段;即价格、数量和“cost”。因为这是数据的属性,而不是组件的属性。 - trk
2个回答

6

使用 vuex。你的 vuex store 将提供一个 getters 对象,可以包装到多个组件的本地 computed 对象中,或直接访问。你的代码将是DRY、响应式、缓存和可维护的。

从我的经验来看,一旦你需要超越 child-parent 数据关系,vuexstoreshared state 是正确的选择。一旦你掌握了它,你的应用程序就会变得神奇起来。

展示如何安装 vuex 已经超出了问题的范围。请访问https://vuex.vuejs.org/guide/getters.html,了解 getters 如何与计算属性类似,并具有在组件之间共享的价值。官方的 Vuex 指南还将演示如何使用 store 初始化您的 Vue 实例。

以下是一些片段,展示了 vuex 系统中的角色。

Store 和 State

// state definition (basically a shared reactive 'data' object that lives outside components)
state:{
    message:'Hello'
}

// the store getters are declared as methods and accessed as properties (just like component/computed)
getters:{
    message: state => return state.message
}

从组件访问

// component 1 wraps getter
computed:{
    message(){
      return this.$store.getters.message
    }
}

// component 2 also wraps getter
computed:{
    message(){
      return this.$store.getters.message
    }
}

// templates can also use getters directly
<div>{{$store.getters.message}}</div>

// If message was wrapped, you can simply use the computed property
<div>{{message}}</div>

一旦开始使用vuex,就会有很多其他的好处出现,比如Chrome中的开发者工具、撤销/重做支持、状态简单重构、时间旅行调试、应用程序持久性等等。此外,还有快捷方式将多个存储getter添加到计算属性中。


如果存在子父级数据关系,可能提供/注入将是一种选项。 - Sphinx
即使官方Vue文档也建议在此应用程序中不要使用该架构而是使用Vuex。但是,注入确实提供了实现解耦的功能;不过这是另一个问题。 - Steven Spungin
谢谢 - 你能给我指一下文档,展示一个“可以被包装成多个组件的对象”的本地计算对象的例子吗?我现在已经把这些数据存储在一个“store”中了,但是不确定如何像之前的“mixin”一样使用原型属性。 - d-_-b
当然,我会添加一个示例来回答。 - Steven Spungin
1
@d-_-b 我添加了一些示例。在Vue开发中,花费时间学习Vuex将会带来比其他任何努力都更多的回报。 - Steven Spungin
Vue 3.0 开始,getter 的结果不像计算属性那样被缓存。这是一个已知问题,需要 Vue 3.1 进行修复。https://next.vuex.vuejs.org/guide/getters.html#getters - Raine Revere

2

正如 @Sphinx 建议的那样,您可以使用 ref 来访问子组件。

例如:

<template>
  <div id="list">
     <product-item v-for="product in sorted" :product="product" :ref="product"></product-item>
  </div>
</template>
<script>
import ProductItem from './Product.vue'
export default {
   components: { ProductItem }, 
   data: () => ({
       hidrated: false,
       items: []
   })
   created() {
       this.items = [...] // as noted above
   },
   mounted() {
       this.hidrated = true
   },
   computed: {
       sorted () {
           if (!this.hidrated && !Object.keys(this.$refs).length) {
               // handle initial state, before rendered
               return this.items
           }
           return Object.values(this.$refs)[0]
               .sort((a,b) => b.cost - a.cost)
               .map(c => c.product)
       }
   }
}
</script>

假设您的List Component中没有其他ref,则可以这样做。

您还需要检查组件是否已渲染,此处我使用hidrated标记组件何时被挂载。


2
你可以直接使用ref="product",然后this.$refs.product将是一个数组。查看Vue指南:ref,它说明当在具有v-for的元素/组件上使用时,注册的引用将是包含DOM节点或组件实例的数组。 - Sphinx
感谢 @Sphinx 指出这一点。我已经测试过并更新了我的答案。 - Wildan Maulana Syahidillah
可能更好的写法是 return this.$refs.product .sort((a,b) => b.cost - a.cost) .map(c => c.product)。您也应该相应地更新描述。 - Sphinx
this.$refs.product returns undefined. this.$refs returns {[object Object]: Array[2]} - Wildan Maulana Syahidillah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接