【问题标题】:Dynamic component event bus not work in vue?动态组件事件总线在 vue 中不起作用?
【发布时间】:2018-10-24 23:03:14
【问题描述】:

我只是在 vuejs 中将事件总线用于动态组件,但是当我从一个组件发出事件时,我无法在另一个组件中监听该事件。下面是我通过codesandbox创建的演示,我想做的是从test2component输出'test in tes2',但它什么也没输出。但是当我在 HomeCompnent 中取消注释 $bus 监听器时,两个 console.log 都被执行了。

main.js

Vue.propotype.$bus = new Vue();

主页组件

<template>
  <div>
    <component :is="currentComponent" @changeComponent="changeComponent"></component>
  </div>
</template>

<script>
import Test1Component from "./Test1Component";
import Test2Component from "./Test2Component";

export default {
  created() {
    // this.$bus.$on("test", () => console.log("event bus test"));
  },
  components: {
    Test1Component,
    Test2Component
  },
  data() {
    return {
      currentComponent: "Test1Component"
    };
  },
  methods: {
    changeComponent() {
      this.currentComponent = "Test2Component";
    }
  }
};
</script>

<style>

</style>

测试1组件

<template>
  <div>
    <h1>test1</h1>
    <button @click="changeComponent">click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    changeComponent() {
      this.$emit("changeComponent");
      this.$bus.$emit("test");
    }
  }
};
</script>

<style>

</style>

测试2组件

<template>
  <h1>test2</h1>
</template>

<script>
export default {
  created() {
    this.$bus.$on("test", () => console.log("test in test2"));
  }
};
</script>

<style>

</style>

【问题讨论】:

  • 我对这个问题投了反对票,因为寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误以及重现它所需的最短代码 在问题本身。链接到远程站点不满足此要求,因为(1. 链接可能会中断(2. 其他站点可能无法提供所有可访问性调整)
  • @Ferrybig 抱歉,我已经编辑了问题

标签: vue.js vuejs2 vuex


【解决方案1】:

你的测试不正确。

基本上Test2Component不会在Test1Component发出this.$bus.$emit("test");时创建

您看到了 2 个 console.log,因为 codesandbox 使用热重载模块,所以如果您更改代码,this.$bus.$on("test", () =&gt; console.log("test in test2")); 仍会注册。

如果您在Test2Component 中的组件销毁时取消注册,则永远不会记录test in test2

Test2Component.vue

<template>
  <div>
    <h1>test2</h1>
    <button @click="changeComponent">click me</button>
  </div>
</template>

<script>
export default {
  created() {
    console.log("Component 2 is created");
    this.$bus.$on("test", this.testLog);
  },
  beforeDestroy() {
    this.$bus.$off("test", this.testLog);
  },
  methods: {
    testLog() {
      console.log("test in test2");
    },
    changeComponent() {
      this.$emit("changeComponent");
    }
  }
};
</script>
<style>

</style>

演示:https://codesandbox.io/s/2485jw460y

如果你评论

  beforeDestroy() {
    this.$bus.$off("test", this.testLog);
  },

在组件 2 中,您将看到 test in test2 在几次点击后被多次打印

(如果更改代码,请刷新从头开始运行)

【讨论】:

    【解决方案2】:

    这个问题很好地证明了事件总线模式的缺点:

    作为开发人员,您必须小心确保所有事件的发送者和接收者实际上同时存在。

    在您的情况下,情况并非如此:

    • 当 Test1Component 发出事件时,Test2Component 还不存在。
    • 在不久之后 HomeComponent 创建了 Test2Component 之后,该事件已经“消失”了。

    作为 Vue 核心团队成员,我通常的免责声明:不要使用事件总线模式。

    【讨论】:

    • 好像没看懂事件总线模式。谢谢。
    【解决方案3】:

    事件总线很好,但问题在于实现。

    你是非常正确的un-comment code 在家庭组件中,我在你的 sn-p 上测试了它只发出一次 event bus test [console.log]

    对于事件,您需要注意这一点

    1. 您需要定义监听器。 [确保在发出事件之前定义它们]
    2. 现在您只需发出事件。 [为了工作,我们需要听众先听]

    在你的情况下,你在Test2Componentcreated 事件中写了listener function $on

    现在想想,你一开始没有任何听众,就像home compo你刚刚评论了那个听众代码。[它的初始步骤]

    现在,当您单击click me 按钮时,您是changing 组件到新组件[Test2Component],它已安装并且其created 事件将触发then this listener will start listening to event

    但你错过了这个

    this.$emit("changeComponent"); // this is first [ fires/emit ]
    this.$bus.$emit("test"); // THEN THIS EMIT
    

    所以,当它刚刚从compo1 tocompo2testfired/emitted directly without wait, its notsynchronousit isAsynchronousIt will not wait to finish all stuff ofchangeComponent. It will be emitted immediately [test`]启动changing组件时p>

    猜猜是什么现在当test发射时,那时Dom operation to add component, ITS NOT DONE YETtest发射

    所以,No listeners are there,所以没有console.log

    但是如果你看看,你在 home 函数监听器中的 UNCOMMENT listeneremit of test event 之前定义好,所以它在控制台中输出。

    希望你能理解,如果不知道点我会详细解释的

    另一个细节是您在prototype 中添加了 $bus,因此所有组件都有自己的总线。相反,您可以使用GLOBAL event Bus

    如你所见。

    在 es6 中你可以做到

    //bus.js
    const bus = new Vue({});
    export default bus;
    

    将其导入其他组件

    import bus from './bus.js';
    
    // ... do bus.$on ..
    // ... do bus.$emit ..    
    

    var $bus = new Vue({});
    
    Vue.component('Test1', {
    	template: `
    		<div class="blog-post">
    			<h3>test1</h3>
          <button @click="changeComponent">click me</button>
    		</div>
    	`,
      methods: {
        changeComponent() {
          $bus.$emit("chng");
          $bus.$emit("test");
        }
      }
    });
    
    Vue.component('Test2', {
    	template: `
    		<div class="blog-post">
    			<h3>test2</h3>
    		</div>
    	`,
      created() {
        $bus.$on("test", () => console.log("test in test2 will not fire as we are little late to listen it"));
      }
    });
    
    
    new Vue({
    	el: '#app',
    	
    	created: function(){
    	 console.log('created');
       $bus.$on("chng", () => this.changeComponent());
       $bus.$on("test", () => console.log("test in test2 main/HOME"));
    	},
      data() {
        return {
          currentComponent: "Test1"
        };
      },
      methods: {
        changeComponent() {
          this.currentComponent = "Test2";
        }
      }
    });
    <!DOCTYPE html>
    <html>
    <head>
    	<script> console.info = function(){} </script>
    	<script src="https://code.jquery.com/jquery.min.js"></script>
    	<script src="https://vuejs.org/js/vue.js"></script>
    	<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
    	<meta charset="utf-8">
    	<meta name="viewport" content="width=device-width">
    	<title>Stack Overflow - Hardik Satasiya</title>
    	<style>.half{width:50%;float:left;}</style>
    </head>
    <body>
    	<div id="app">
    		<component :is="currentComponent" @changeComponent="changeComponent"></component>
    	</div>
    </body>
    </html>

    【讨论】:

      猜你喜欢
      • 2018-12-01
      • 2018-08-20
      • 1970-01-01
      • 1970-01-01
      • 2019-05-26
      • 2017-11-21
      • 1970-01-01
      • 2017-04-13
      • 2018-12-14
      相关资源
      最近更新 更多