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

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

本文关键字:组件 之间 共享 方法 Vue js      更新时间:2023-09-26

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

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 路由器,感谢您的任何帮助!

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

SRC/共享.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)

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

这是你的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',
});

这是这种方法的Plunker。

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

如果您尝试在多个 vue 模板和布局之间或沿多个 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>

共享.js

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

您可以将该方法放在根 Vue 实例中,然后在选择蔬菜或选择水果时从子实例调度事件。 事件在其父组件上查找处理程序,如果找不到事件处理程序,则它们将继续沿链向上移动,直到找到为止。 因此,在您的根实例上:

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

然后在子实例上:

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

在 Vue 3 中,你可以使用 Composables 来重用代码

js/Composables/Capitalize.js

export function useCapitalized(name) {
    const capitalizedFirst = name[0].toUpperCase();
    const rest = name.slice(1);
    return capitalizedFirst + rest;
}

js/Components/YourComponent.vue

<template>...</template>
<script>
import { useCapitalized } from '@/Composables/Capitalize.js'; // by convention, composable function names start with "use"
export default {
    props: {
        statusName: String,
    },
    data() {
        return {
            content: "",
        }
    },
    created() { 
        this.content = useCapitalized(this.statusName)
    }
}
</script>

Vue 3 关于使用 Composables 在组件之间重用代码的官方文档 这里 选项 API 和这里 Composition API

我只会配置一个混合

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

这很有用,因为使用 aproach 只是从 js 文件导入函数,正在导入的文件中的函数将采用自己的"this"在函数中使用,这可能会导致一些混淆,而 mixin 将使用它正在导入的组件的"this",就好像它的函数是组件的一部分, 正如我所说。

注意:如果共享文件中有 2 个(或更多)方法.js,则可以使用以下方法从另一个方法调用一个方法:

utils.js:

export default {
  method1(){
    ···
    return ...
  },
  method2(){
    val1 = this.method1();
    ···
    return ...
  },
}

yourcomponent.vue

<template>...</template>
<script>
  import utils from './utils.js';
  export default {
    created() { 
      val2 = utils.method2();
    }
  }
</script>