【问题标题】:Vue2 event bus working after second callVue2事件总线在第二次调用后工作
【发布时间】:2017-12-18 06:07:50
【问题描述】:

我有一个嵌套组件,子组件应该从主实例接收一个参数,但问题是我必须调用该事件两次才能获取参数。

index.html

<div id="app">
    <button @click="displayComponent">Display</button><br/><hr/>
    {{ message }}

    <mycomponent v-if="showComponent" @hide="hideComponents"></mycomponent>
</div>

code.js

window.bus = new Vue();

Vue.component('mycomponent', {
    template: `
        <div>
            <h3>Im the parent component</h3>
            <childcomponent></childcomponent>
            <button @click="$emit('hide')">Hide components</button>
        </div>
    `
});

Vue.component('childcomponent', {
    template:`
        <div>
            <h4>Im the child component</h4>
            <p>{{ data }}</p>
        </div>
    `,

    data() {
        return {
            text: 'Nothing loaded'
        };
    },

    methods: {
        test() {
            alert('hello');
        },

        getData(x) {
            this.text = x;
        }
    },

    created(){
        bus.$on('extraCall', data => {
            this.getData(data);
            this.test();
        });
    }
});

const app = new Vue({
    el: '#app',

    data: {
        message: 'hello world!',
        showComponent: false
    },

    methods: {
        displayComponent() {        
            bus.$emit('extraCall', 'this is some extra text');
            this.showComponent = true;
        },

        hideComponents() {
            this.showComponent=false;
        }
    }
});

子组件元素内的文本值设置为默认值,单击“显示”按钮后,它会触发 bus.$emit 并带有一些文本作为参数的 extraCall 事件,这应该会更新文本值,并且它仅在第二次单击“显示”按钮后才会发生。

我错过了什么?

【问题讨论】:

    标签: javascript html vue.js event-handling vuejs2


    【解决方案1】:

    &lt;mycomponent&gt;(及其子 &lt;childcomponent&gt;)在单击“显示”按钮时未实例化,因为 v-if="showComponent"。

    第一次点击:

    1. extraCall 在总线上发出,但该事件没有侦听器,因此被忽略。
    2. &lt;mycomponent&gt; 在将showComponent 设置为true 后被实例化。
    3. &lt;mycomponent&gt; 在其created 钩子中为extraCall 事件注册一个监听器。

    第二次点击:

    1. extraCall 在总线上发出,&lt;mycomponent&gt; 处理它。

    您可能认为应该交换 bus.$emit() 和 this.showComponent = true 行,以便 &lt;mycomponent&gt; 在事件发出之前被实例化,但这仍然行不通,因为 Vue 推迟了创建直到下一个微任务更新视图。

    这可能有效:

    displayComponent() {
      this.showComponent = true;
    
      // Wait for child component to be instantiated
      this.$nextTick(() => {  
        bus.$emit('extraCall', 'this is some extra text');
      });
    }
    

    如果上面的代码对你有用,我仍然不推荐它。在发出事件之前,您不需要考虑子组件的创建(它将您的组件耦合在一起)。您应该以其他方式共享数据,查看其他关于跨组件共享数据的最佳方式的 SO 问题。

    【讨论】:

    • 好的,这确实有效,但正如你所说的不是最佳的,必须有更好的解决方案。但由于它解决了主要问题,我将其作为解决方案投票,如果我找到更好的解决方案,我会在此处发布。非常感谢!
    • 好吧,既然你真的很好,很合作,我会问你这个,前段时间我做了这样的事情并且它有效,但现在它没有,我发现的唯一区别是在 html 文件中,显示组件而不是 v-if 的条件是 v-show,当我更改它时,它就可以工作了......使用这个会产生任何附带损害吗?...... AFAIK v-show 需要更多加载,然后只显示正确,并且 v-if 每次调用时都会创建组件。
    • v-show 仅在元素上切换样式。如果v-show 为假,组件仍然被创建,只是因为样式而被隐藏(您可以在开发工具中看到它)。 v-if 实际上会破坏/创建元素。如果您在这里使用v-show 而不是v-if,它将适用于您的原始代码。要在 v-if 和 v-show 之间做出决定,您需要考虑 (1) 您是否需要始终创建元素? (2) 将元素隐藏在 DOM 中会影响任何:nth-child CSS 选择器吗? (3) 使用v-show 保持元素活动将使用更多内存等。
    • 非常感谢您抽出宝贵时间,现在我很清楚了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    • 2013-11-11
    • 2017-03-29
    • 2022-01-06
    相关资源
    最近更新 更多