Как я могу поделиться методом между компонентами в Vue.js?

Ознакомьтесь с этой простой демо-версией корзины покупок:

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

Пользователь может выбрать veggie и фрукты, и он будет добавлен в корзину. Функция, которая добавляет фрукты /veggie, очень похожа, и я хочу объединить ее в функцию, которая может быть разделена между обоими компонентами.

    selectFruit: function(product){
       var cart = this.cart
       for(p in cart){
       if (cart[p]["type"] == "fruit"){
           console.log("We already got a fruit!, Let 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 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 Router с этим проектом, спасибо за любую помощь!

Ответ 1

Я нашел эту технику более простой/удовлетворительной, так как я предпочитаю композицию, а не наследование:

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-scope, замените this.foo = shared.foo на this.foo = shared.foo.bind(this)

Ответ 2

Вариант 1

Один из способов совместного использования вашего метода между компонентами - использовать mixin. Здесь a cartMixin, который содержит метод selectProduct:

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

Здесь вилка вашего плункера.

Вариант 2

Подумав об этом, еще один вариант, который вы можете рассмотреть, - создать базовый компонент Product и расширить его, чтобы создать компоненты Fruit и Vegetable. Затем вы поместите свою общую функциональность в базовый компонент.

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 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',
});

Здесь Plunker с таким подходом.

Учитывая, что ваши шаблоны Fruit and Vegetable настолько похожи, вы могли бы принять эту идею еще дальше и использовать общий шаблон из базового компонента.

Ответ 3

Вы можете поместить этот метод в свой корневой экземпляр Vue, а затем отправить событие из дочернего экземпляра, когда выбран veggie или когда выбран фрукт. События ищут обработчик на их родительском компоненте, и если они не находят обработчик событий, они продолжают подниматься по цепочке до тех пор, пока они не сделают это. Итак, в корневом экземпляре:

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

        //handle the choosing of fruit

    }
}

Затем на дочернем экземпляре:

selectFruit: function(product){

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

}