如何在 Vue.js 中的组件之间共享方法?

Posted

技术标签:

【中文标题】如何在 Vue.js 中的组件之间共享方法?【英文标题】:How can I share a method between components in Vue.js? 【发布时间】:2016-05-04 20:41:55 【问题描述】:

查看这个简单的购物车演示:

http://plnkr.co/edit/CHt2iNSRJAJ6OWs7xmiP?p=preview

用户可以选择蔬菜和水果,它们将被添加到购物车数组中。添加水果/蔬菜的函数非常相似,我想将它组合成一个可以在两个组件之间共享的函数。

    selectFruit: function(product)
       var cart = this.cart
       for(p in cart)
       if (cart[p]["type"] == "fruit")
           console.log("We already got a fruit!, Let's remove " + cart[p]["name"] + " and add in " + product["name"]);
              this.cart.$remove(cart[p])
             
            
            console.log("Adding " + product.name + " to cart.");
            var productName = product.name
            var cartFruit = name: product.name, type: 'fruit'
            this.cart.push(cartFruit)


selectVeggie: function(product)
    var cart = this.cart
    for(p in cart)
        if (cart[p]["type"] == "veggie")
           console.log("We already got a veggie!, Let's remove " + cart[p]["name"] + " and add in " + product["name"]);
           this.cart.$remove(cart[p])
        
    
    console.log("Adding " + product.name + " to cart.");
    var productName = product.name
    var cartVeggie = name: product.name, type: 'veggie'
    this.cart.push(cartVeggie)

我怎样才能做到这一点,以便我可以改变这个方法并让它在全球范围内使用?顺便说一句,我在这个项目中使用 Vue 路由器,感谢您的帮助!

【问题讨论】:

【参考方案1】:

我只会配置一个 mixin

export const sharedMethods =  
    // Generic funcion
    methods: 
        axiosGet(route)
            return axios.get(route)
            .then(response => response.data)
            .catch((error)=> console.log(error))
        ,
        axiosPost(route, postObj, resolveCallback, rejectCallback)
            axios.post(route, postObj)
            .then(resolveCallback)
            .catch(rejectCallback);
        ,

然后在组件中使用 mixin,调用它的函数,就像调用组件本身的函数一样,就好像这些函数是你要导入 mixin 的组件的一部分:

放在组件上:mixins: [sharedMethods],

在组件内部,我调用了mixin函数:

this.axiosPost(
      '/route',
      data,
      () => console.log('success'),
      () => console.log('fail')
)

这很有用,因为使用一种方法,您只需从 js 文件中导入函数,正在导入的文件中的函数将在函数中使用它们自己的“this”,这可能会导致一些混乱,而 mixin 将使用它被导入的组件的“this”,就像我说的那样,它的功能是组件的一部分。

【讨论】:

【参考方案2】:

如果您尝试在多个 vue 模板和布局之间或沿其共享相同的组件逻辑,您可以简单地尝试以下方法:

之前:

a.vue

<template>
  <div>
     <h1>title</h1>
     <small>datetime</small>
  </div>
</template>

<script>
export default 
  props: 
    title: String
  ,
  data() 
    return 
      datetime: new Date()
    
  

</script>

b.vue

<template>
  <div>
     <h3>title</h3>
     <h6>datetime</h6>
  </div>
</template>

<script>
export default 
  props: 
    title: String
  ,
  data() 
    return 
      datetime: new Date()
    
  

</script>

之后:

a.vue

<template>
  <div>
     <h1>title</h1>
     <small>datetime</small>
  </div>
</template>

<script>
import shared from "./shared.js";
export default Object.assign(, shared);
</script>

b.vue

<template>
  <div>
     <h3>title</h3>
     <h6>datetime</h6>
  </div>
</template>

<script>
import shared from "./shared.js";
export default Object.assign(, shared);
</script>

shared.js

export default 
  props: 
    title: String
  ,
  data() 
    return 
      datetime: new Date()
    
  


【讨论】:

【参考方案3】:

我发现这种技术更简单/令人满意,因为我更喜欢组合而不是继承:

src/shared.js

export default 
  foo: function()  alert("foo!") 

src/yourcomponent.vue

<template>...</template>

<script>
  import shared from './shared'

  export default 
    created()  
      this.foo = shared.foo // now you can call this.foo() (in your functions/template)
    
  
</script>

这也将允许您编写与 Vue 无关的测试。

注意:如果您需要 foo 在 Vue 范围内运行,请将 this.foo = shared.foo 替换为 this.foo = shared.foo.bind(this)

【讨论】:

我认为应该是import shared from './shared',不带引号的shared 为了在模板中使用它,您可以将函数添加到您的方法中,例如 export default name: 'componentName', methods: shared, @GiorgioTempesta 可以添加更多方法吗? 是的,只要您导入它们并用逗号分隔每个方法即可。此外,如果您需要使用不同的名称调用该方法,则必须将其声明为:methods: 'new_method_name': shared, 'other_method_name': shared2 您好,请问如何使用这种方法进行异步,语法是什么?是 export default async foo: function() alert("foo!") 【参考方案4】:

选项 1

跨组件共享方法的一种方法是使用 mixin。这是一个包含selectProduct 方法的cartMixin

var cartMixin = 
  methods: 
    selectProduct: function (product) 
      var cart = this.cart
      for(p in cart)
          if (cart[p]["type"] == product.type)
             console.log("We already got a "+ product.type +"!, Let's remove " + cart[p]["name"] + " and add in " + product["name"]);
             this.cart.$remove(cart[p])
          
      
      console.log("Adding " + product.name + " to cart.");
      var productName = product.name
      var cartProduct = name: product.name, type: product.type
      this.cart.push(cartProduct)
    
  
;

您可以像这样在每个组件中引用它:

var Vegetable = Vue.extend(
    template: '#vegetable',
    mixins: [cartMixin],
    data: function()
        return sourceOfTruth
    
)

...然后像这样在您的模板中使用它:

<li v-for="product in food | showOnly 'fruit'" @click="selectProduct(product)">
  product.name
</li>

Here's a fork of your Plunker.

选项 2

在考虑了更多之后,您可能会考虑的另一个选择是创建一个基本的Product 组件并将其扩展以创建您的FruitVegetable 组件。然后,您可以将常用功能放入基本组件中。

var Product = Vue.extend(
  data: function()
      return sourceOfTruth
  ,
  methods: 
    selectProduct: function (product) 
      var cart = this.cart
      for(p in cart)
          if (cart[p]["type"] == product.type)
             console.log("We already got a "+ product.type +"!, Let's remove " + cart[p]["name"] + " and add in " + product["name"]);
             this.cart.$remove(cart[p])
          
      
      console.log("Adding " + product.name + " to cart.");
      var productName = product.name
      var cartProduct = name: product.name, type: product.type
      this.cart.push(cartProduct)
    
  
)

var Vegetable = Product.extend(
  template: '#vegetable',
);
var Fruit = Product.extend(
  template: '#fruit',
);

Here's a Plunker with this approach.

鉴于您的水果和蔬菜模板非常相似,您或许可以更进一步地利用这个想法,并使用基础组件中的通用模板。

【讨论】:

我认为选项 1 是这里最“正确”的 vue 答案......虽然 Mixins 有一个不幸的名字 imo 不清楚。也许应该称为 AddIns、AddOns、Extenders、ShareMes 等。【参考方案5】:

您可以将该方法放在您的根 Vue 实例中,然后在选择蔬菜或选择水果时从子实例调度一个事件。事件在它们的父组件上寻找一个处理程序,如果他们没有找到一个事件处理程序,他们会继续往上走,直到找到为止。所以在你的根实例上:

events: 
    'choose-fruit':function(fruit)

        //handle the choosing of fruit

    

然后在子实例上:

selectFruit: function(product)

    this.$dispatch('choose-fruit', product);


【讨论】:

请注意,$dispatch 在 Vue 2.0 中已被删除,尽管您可以使用 $emit$on$off 来替换它:vuejs.org/v2/guide/…

以上是关于如何在 Vue.js 中的组件之间共享方法?的主要内容,如果未能解决你的问题,请参考以下文章

在所有 Vue.js 组件之间共享数据

如何在多个 Angular 2 项目之间共享一个 Angular 2 组件?

如何在 Monorepo 中的 VueJS 项目之间共享组件

什么是vuex?他有什么作用?如何改变store中的状态(state)?

Vue JS 3:如何将数据从一个组件传递到另一个组件?

在 Node.js 和 Vue.js 之间共享 TypeScript 代码