从父组件到子组件的事件传递

7

我有一个在父组件中生成的事件,子组件必须对其做出反应。我知道这不是vuejs2推荐的方法,我必须进行一个$root emit,这非常糟糕。所以我的代码是这样的。

<template>
    <search></search>
    <table class="table table-condensed table-hover table-striped" v-infinite-scroll="loadMore" infinite-scroll-disabled="busy" infinite-scroll-distance="10">
        <tbody id="trans-table">
            <tr v-for="transaction in transactions" v-model="transactions">
                <td v-for="item in transaction" v-html="item"></td>
            </tr>
        </tbody>
    </table>
</template>

<script>
    import Search from './Search.vue';

    export default {
        components: {
            Search
        },
        data() {
            return { 
                transactions: [], 
                currentPosition: 0 
            }
        },
        methods: {
            loadMore() {
                this.$root.$emit('loadMore', {
                    currentPosition: this.currentPosition
                });
            }
        }
    }
</script>

正如您所看到的,loadMore在无限滚动时被触发,并且事件被发送给子组件“search”。不仅是搜索,因为它是根组件,所以它会广播给所有人。

这种情况下有更好的方法。我知道我应该使用props,但我不确定如何在这种情况下做到这一点。


2
你可以直接在子组件中使用 this.$children[index].$emit 进行事件的触发:https://vuejs.org/v2/api/#vm-children - Potray
搜索组件是子组件吗?你可以添加一个属性吗? - Roy J
@RoyJ 是的,搜索是一个子组件,你可以在我提供的代码中看到,在第2行有一个<search></search>模板,我可以添加一个prop,但我的问题是如何使用prop来触发loadMore? - madeye
2个回答

28

只需要有一个变量(称为moreLoaded),每次调用loadMore就将其递增。将变量和currentPosition一起作为属性传递给你的search组件。在Search中,你可以watch moreLoaded并相应地采取行动。

更新
低级? 我的解决方案? 哦,我从来没有! ;)

你也可以使用本地事件总线。设置它大致如下:

export default {
    components: {
        Search
    },
    data() {
        return {
            bus: new Vue(),
            transactions: [], 
            currentPosition: 0 
        }
    },
    methods: {
        loadMore() {
            this.bus.$emit('loadMore', {
                currentPosition: this.currentPosition
            });
        }
    }
}

并将其传递给搜索功能:

<search :bus="bus"></search>

需要将 bus 作为属性(当然),并且有一个类似于以下的部分

created() {
    this.bus.$on('loadMore', (args) => {
        // do something with args.currentPosition
    });
}

那肯定可以工作,但感觉有点不正规。 - madeye
在我看来,这是一个很好的答案。我不会称之为hacky,因为它只是利用了事件总线模式,并以一个小的vue对象作为基础。 - Mike R
5
我建议在状态被销毁时也删除监听器,就像这里所解释的那样:https://www.digitalocean.com/community/tutorials/vuejs-global-event-bus。否则,如果你的对象被销毁并重新创建,旧对象的侦听器仍将存在,可能会导致奇怪的错误!(我刚刚遇到了这个烦人的bug)。要销毁你的监听器,请给你的回调函数命名,然后执行 beforeDestroy: function() {this.bus.$off('loadMore', nameOfCallback);} - tobiasBora

1
我认为@tobiasBora对@Roy J的回答的评论非常重要。如果您的组件被创建和销毁多次(例如使用v-if),则处理程序将被调用多次。此外,即使组件被销毁,处理程序也将被调用(这可能会变得非常糟糕)。
正如@tobiasBora所解释的那样,您必须使用Vue的$off()函数。对我来说,这最终变得不太容易,因为它需要参考事件处理程序函数。我最终做的是将此处理程序定义为组件数据的一部分。请注意,这必须是箭头函数,否则您需要在函数定义之后使用.bind(this)。
export default {
    data() {
        return {
            eventHandler: (eventArgs) => {
                this.doSomething();
            }
        }
    },
    methods: {
        doSomething() {
            // Actually do something
        }
    },
    created() {
        this.bus.$on("eventName", this.eventHandler);
    },
    beforeDestroy() {
        this.bus.$off("eventName", this.eventHandler);
    },
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接