【问题标题】:How can I share a method between components in Vue.js?如何在 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 路由器,感谢您的帮助!

【问题讨论】:

    标签: javascript vue.js


    【解决方案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 组件并将其扩展以创建您的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'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/…
        猜你喜欢
        • 2018-07-01
        • 2018-12-26
        • 2021-11-11
        • 2019-08-14
        • 2020-09-23
        • 2019-08-18
        • 1970-01-01
        • 2011-12-25
        相关资源
        最近更新 更多