How can I share a method between components in Vue.js?

匿名 (未验证) 提交于 2019-12-03 02:49:01

问题:

Check out this simple shopping cart demo:

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

A user can pick a veggie and a fruit, and it will be added into the cart array. The function that adds a fruit/veggie is very similar, and I want to combine it into a function that can be shared across both components.

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

How can I make it so I can alter this method and have it used globally? I'm using the Vue Router with this project btw, thanks for any help!

回答1:

Option 1

One approach for sharing your method across components is to use a mixin. Here's a cartMixin that contains a selectProduct method:

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

You can reference this in each component like this:

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

... and then use it in your templates like this:

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

Here's a fork of your Plunker.

Option 2

After thinking about this some more, another option you might consider is to create a base Product component and extend that to create your Fruit and Vegetable components. You would then put your common functionality in the base component.

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.

Given that your Fruit and Vegetable templates are so similar, you might be able to take this idea even further and use a common template from the base component.



回答2:

I found this technique to be more simple/satisfactory, as i prefer composition over inheritance:

src/shared.js

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

src/yourcomponent.vue

<template>..</template>  <script>   import 'shared' from './shared'    export default {     mounted: function(){        this.foo = shared.foo // now you can call this.foo() (in your functions/template)     }   } </script> 

This will easify writing vue-agnostic tests as well.

NOTE: if you need foo to run in vue-scope replace this.foo = shared.foo with this.foo = shared.foo.bind(this)



回答3:

You can put the method in your root Vue instance and then dispatch an event from the child instance when a veggie is selected, or when a fruit is selected. Events look for a handler on their parent component, and if they don't find an event handler they keep going up the chain until they do. So on your root instance:

events: {     'choose-fruit':function(fruit){          //handle the choosing of fruit      } } 

Then on the child instance:

selectFruit: function(product){      this.$dispatch('choose-fruit', product);  } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!