动态组件中用总线Bus的坑

在我们的项目总难免会遇到用动态组件,这里就拿vue官方的例子为例,我们欲在组件中添加总线bus(其实官方推荐的vuex更好用,但是有时候我们只需要传一个小状态,不需要用vuex),首先要mian.js 中创建一个总线Bus(当然这里一般要把Bus封装一下放在一个单独的js中,这里单纯只是为了演示,就在main.js中创建一个全局的EventBus)

import Vue from 'vue'
import App from './App'
import router from './router' window.EventBus = new Vue()
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})

然后我们在动态组件Tabhome中写个按钮触发emit事件,在触发的时候把我们想要传的值一并带过去。

<template>
<div>
<div>
<button @click="handleClick">触发</button>
</div>
</div>
</template> <script>
export default {
name: 'TabHome',
data () {
return {
msg: 'home data '
}
},
methods: {
handleClick () {
window.EventBus.$emit('getData', this.msg)
}
} }
</script>

然后我们在我们想在接受值的地方监听触发的这个函数,我这里拿TabPosts来监听Tabhome中触发的函数,注意这两个组件是动态组件,不是通过路由切换的,监听组件如下:

<template>
<div>
{{post}}
</div>
</template> <script>
export default {
name: 'TabPosts',
data () {
return {
post: 'tabposts',
}
},
methods: {
getData (msg) {
this.post = msg
}
},
mounted () {
window.EventBus.$on('getData', (msg) => this.getData(msg))
}
}
</script>

我们期望的结果当然是我们在tabhome中点击了按钮之后,当我们切换到TabPosts组件的时候,TabPosts中的值已经发生了改变,也就是从tabhome中传过来的值,但是情况远非我们想的这么简单,在监听函数中添加console你就会发现,第一次点击按钮,并切换到TabPosts组件的时候,不会打印任何东西,也就是没有触发mounted钩子。当你切回去tabhome组件再次点击按钮,然后再回到TabPosts组件,发现控制台有输出,但是随着来回切换次数的增多,控制台每次打印的数量也会随着你切换的次数一次递增,但是数据发生了改变,视图却没有改变。

这是为什么呢?这就是动态组件的坑,因为我用的是生命周期的钩子函数,监听函数要在触发函数之前存在 ,不然当然监听不到了。问题就出在生命周期函数这里。所以我们来在两个组件中加上所有的生命周期钩子并在里面输出识别信息。tabhome组件如下:

<template>
<div>
<div>
<button @click="handleClick">触发</button>
</div>
</div>
</template> <script>
export default {
name: 'TabHome',
data () {
return {
msg: 'home data '
}
},
methods: {
handleClick () {
window.EventBus.$emit('getData', this.msg)
}
},
beforeCreate () {
console.log('A beforecreate')
},
created () {
console.log('A created')
},
beforeMount () {
console.log('A beforemount')
},
mounted () {
console.log('A mounted')
},
beforeUpdate () {
console.log('A before update')
},
updated () {
console.log('A updated')
},
beforeDestroy () {
console.log('A before destroy')
},
destroyed () {
console.log('A beforecreate')
} }
</script>

TabPosts组件如下(为了在控制台明显区分,在这里给TabPost组件打印的东西加上黄色的背景色):

<template>
<div>
{{post}}
<router-link to="/TabHome">return</router-link>
</div>
</template> <script>
export default {
name: 'TabPosts',
data () {
return {
post: 'tabposts',
number: 0
}
},
methods: {
getData (msg) {
this.post = msg
}
},
beforeCreate () {
console.log('%c%s',
'background: yellow;',
'B beforecreate')
},
created () {
console.log('%c%s',
'background: yellow;',
'B created')
},
beforeMount () {
console.log('%c%s',
'background: yellow;',
'B beforemount')
},
mounted () {
console.log('%c%s',
'background: yellow;',
'B mounted')
window.EventBus.$on('getData', (msg) => this.getData(msg))
},
beforeUpdate () {
console.log('%c%s',
'background: yellow;',
'B before update')
},
updated () {
console.log('%c%s',
'background: yellow;',
'B updated')
},
beforeDestroy () {
console.log('%c%s',
'background: yellow;',
'B before destroy')
},
destroyed () {
console.log('%c%s',
'background: yellow;',
'B beforecreate!')
}
}
</script> <style scoped> </style>

然后我们测试从动态组件tabhome到TabPosts组件的这个过程中控制台会打印出什么,结果如下图:

我们会发现,A组件(即是tabhome组件)和B组件(即是TabPosts组件)两个的生命周期函数没有交集也就是说触发emit的时候并没有监听到,所以视图不会改变。至于在动态组件中来回切换会增加触发次数,根据前人的经验,应该是在监听组件B中的beforeDestroy中添加EventBus.$off函数就好了,但是会发下加了这个off之后,就不会触发$on的监听函数了,至于为什么不会监听函数这其中的原因我也不太懂。

目前还没找到动态组件中实现总线Bus的好方法,大佬们有好方法欢迎指正!

组件之间的Bus总线传值

因为动态组件之间的坑,我放弃了用动态组件,改用路由切换的两个组件进行传值。在路由的index.js中加入路由信息

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/Home'
import TabHome from '@/pages/Dynamic-component/components/TabHome'
import TabPosts from '@/pages/Dynamic-component/components/TabPosts' Vue.use(Router) export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
}, {
path: '/tabhome',
name: 'TabHome',
component: TabHome
}, {
path: '/TabPosts',
name: 'TabPosts',
component: TabPosts
}
]
})

但是这其中也有坑,我们由A(即tabhome)组件触发EventBus.$emit 函数,让B(即TabPosts)组件监听EventBus.$on,一般触发函数都会放到click函数中,也就是哪个事件需要就放到哪里,本例子放到click事件中。监听函数一般放到created或者mounted中,这里我放到了mounted中。

A(tabhome)组件代码如下:

<template>
<div>
<div>
<button @click="handleClick">触发</button>
</div>
</div>
</template> <script>
export default {
name: 'TabHome',
data () {
return {
msg: 'home data '
}
},
methods: {
handleClick () {
window.EventBus.$emit('getData', this.msg)
this.$router.push('/TabPosts')
}
},
beforeCreate () {
console.log('A beforecreate')
},
created () {
console.log('A created')
},
beforeMount () {
console.log('A beforemount')
},
mounted () {
console.log('A mounted')
},
beforeUpdate () {
console.log('A before update')
},
updated () {
console.log('A updated')
},
beforeDestroy () {
console.log('A before destroy')
},
destroyed () {
console.log('A beforecreate')
}
}
</script>

B(TabPosts)组件的代码如下:

<template>
<div>
{{post}}
<router-link to="/TabHome">返回</router-link>
</div>
</template> <script>
export default {
name: 'TabPosts',
data () {
return {
post: 'tabposts',
number: 0
}
},
methods: {
getData (msg) {
this.post = msg
}
},
beforeCreate () {
console.log('%c%s',
'background: yellow;',
'B beforecreate')
},
created () {
console.log('%c%s',
'background: yellow;',
'B created')
},
beforeMount () {
console.log('%c%s',
'background: yellow;',
'B beforemount')
},
mounted () {
console.log('%c%s',
'background: yellow;',
'B mounted')
window.EventBus.$on('getData', (msg) => this.getData(msg))
},
beforeUpdate () {
console.log('%c%s',
'background: yellow;',
'B before update')
},
updated () {
console.log('%c%s',
'background: yellow;',
'B updated')
},
beforeDestroy () {
console.log('%c%s',
'background: yellow;',
'B before destroy')
},
destroyed () {
console.log('%c%s',
'background: yellow;',
'B beforecreate!')
}
}
</script> <style scoped> </style>

结果我们按照这个代码运行总是不成功,没有我们想要的效果,上面的代码我加了所有的生命周期的钩子函数,我们从A的按钮切换到B组件,注意留意控制台,当我们点击按钮通通过路由切换到B组件的时候,生命周期函数的变化,我们会发现如下的结果。



我们发现,在A销毁之前,B组件的beforeCreate ,created,和beforeMount这三个钩子函数先触发,之后才是A组件的销毁钩子的触发,因为总线Bus要求要先有监听在触发,才能成功监听,所以我们只能在A组件的beforeDestroy或者destroyed这两个生命周期钩子中触发函数$emit,同理也只能在B组中的beforeCreate ,created,和beforeMount这三个钩子函数中监听$on。

//tabhome (A)组件中在beforeDestroy中触发
beforeDestroy () {
console.log('A before destroy')
window.EventBus.$emit('getData', this.msg)
}
//在TabPosts中的created中监听
created () {
console.log('%c%s',
'background: yellow;',
'B created')
console.log(1)
window.EventBus.$on('getData', (msg) => this.getData(msg))
}

这样我们想要的功能就实现了,实际动手做的细心的同学会发现:还是有之前重复触发的问题,还是会随着切换次数的增加而使监听函数触发的次数增加,解决这个问题就简单了。在我们用总线传值的时候要记得关闭监听,在B组件中的destroyed钩子中增加EventBus.$off方法即可,至此就没问题了。

//TabPosts组件
destroyed () {
console.log('%c%s',
'background: yellow;',
'B beforecreate!')
window.EventBus.$off('getData')
}

vue总线bus传值的一些问题的更多相关文章

  1. python 全栈开发,Day91(Vue实例的生命周期,组件间通信之中央事件总线bus,Vue Router,vue-cli 工具)

    昨日内容回顾 0. 组件注意事项!!! data属性必须是一个函数! 1. 注册全局组件 Vue.component('组件名',{ template: `` }) var app = new Vue ...

  2. vue 父子组件传值以及方法调用,平行组件之间传值以及方法调用大全

    vue项目经常需要组件间的传值以及方法调用,具体场景就不说了,都知道.基本上所有的传值都可以用vuex状态管理来实现,只要在组件内监听vuex就好. vue常用的传值方式以及方法有: 1. 父值传子( ...

  3. 从vue的组件传值着手浅谈观察者模式

    首先,提到观察者模式,这不禁让我想到了MVVM,MVVM架构模式感觉用到了观察者的思想. 我们还是按照惯例,了解一下什么是观察者模式 观察者模式,类似发布订阅模式,完成这个动作首先最少得有两个不同的对 ...

  4. vue组件之间传值方式解析

    vue组件之间传值方式解析一.父组件传到子组件 1.父组件parent代码如下: <template> <div class="parent"> <h ...

  5. vue父子组件传值加例子

    例子:http://element-cn.eleme.io/#/zh-CN/component/form         上进行改的 父传子:用prop:子组件能够改变父组件的值,是共享的,和父操作是 ...

  6. Vue 组件间传值

    前言 Vue 作为现在比较火的框架之一,相信您在使用的过程中,也会遇到组件间传值的情况,本文将讲解几种 Vue 组件间传值的几种方法,跟着小编一起来学习一下吧! 实现 注意: 学习本文,需要您对 Vu ...

  7. Vue通信、传值的多种方式,详解

    Vue通信.传值的多种方式,详解 转自:https://blog.csdn.net/qq_35430000/article/details/79291287 一.通过路由带参数进行传值 ①两个组件 A ...

  8. vue组件常用传值

    一.使用Props传递数据   在父组件中使用儿子组件 <template> <div> 父组件:{{mny}} <Son1 :mny="mny"&g ...

  9. 关于Vue父子组件传值(复杂数据类型的值)的细节点

    vue 父子组件传值是很常见的,多数情况下都是父传递给子的值是基础数据类型,如string,number,boolean, 当父组件值被修改时,子组件能够实时的作出改变. 如果父子传值的类型是复杂数据 ...

随机推荐

  1. PhotoZoom正式版和试用版的区别是什么?

    通常的工具对数码图片进行放大时,总会降低图片的品质,而这款软体使用了S-SPLINE技术(一种申请过专利的,拥有自动调节.进阶的插值算法的技术),可以将尽可能地提高放大图片的品质.程序最大的特色是可以 ...

  2. PhotoZoom的工具栏 图片放大不失真

    使用PhotoZoom能够对数码图片无损放大,备受设计师和业内人员的青睐,它的出现时一场技术的革新,新颖的技术,简单的界面,优化的算法,使得它可以对图片进行放大而没有锯齿,不会失真.本文为您一起来认识 ...

  3. hibernate---crateria

    Leslie 趁还没忘掉,赶快记录下来 Hibernate中Criteria的完整用法 转自:http://www.360doc.com/content/090313/10/26262_2794855 ...

  4. Day 07 -02 拷贝 浅拷贝 深拷贝

    必考 存一个值还是多个值 一个值:整型/浮点型/字符串 多个值:列表/元祖/字典/集合 有序or 无序 有序:字符串/列表/元祖 无序:字典/集合 可变or 不可变 可变:列表/字典/集合 不可变:整 ...

  5. SASS概览

    1.安装: sass需要使用ruby,首先安装ruby,之后: gem install sass 编译: sass input.scss output.css 2.快速入门: 变量: .scss 变量 ...

  6. 基于better-scroll封装一个上拉加载下拉刷新组件

    1.起因 上拉加载和下拉刷新在移动端项目中是很常见的需求,遂自己便基于better-scroll封装了一个下拉刷新上拉加载组件. 2.过程 better-scroll是目前比较好用的开源滚动库,提供很 ...

  7. jQuery中的DatePicker今天按钮不起作用

    转载:http://codego.net/63433/ jquery-ui 日期选择器datepicker我想用 jQueryUI 的 DatePicker ,并显示“今天”按钮, 但它不工作,它也不 ...

  8. HDU 2669 Romantic( 拓欧水 )

    链接:传送门 题意:求解方程 X * a + Y * b = 1 的一组最小非负 X 的解,如果无解输出 "sorry" 思路:裸 exgcd /***************** ...

  9. 查看Linux系统信息命令

    系统 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat /proc/cpuinfo # 查看CPU信息 # ho ...

  10. vue无缝滚动的插件开发填坑分享

    写插件的初衷 1.项目经常需要无缝滚动效果,当时写jq的时候用用msClass这个老插件,相对不上很好用. 2.后来转向vue在vue-awesome没有找到好的无缝滚动插件,除了配置swiper可以 ...