路由组件使用事件总线??

144 阅读2分钟

在路由组件里使用事件总线通信,你没事吧?

但是你执意如此,那么,这里会发生什么呢?

首先我们在list.vue里面写一个emit

this.$router.push('detail')
this.$eventBus.$emit('emit-event')

我们在detaile.vue这个路由组件中监听这个事件

created(){
    console.log('这里是created')
    this.$eventBus.$on('emit-event', ()=>{
        console.log('我监听到喽‘)
    }

好了,先来测试一下结果吧

image.png

嗯,很好,果然没有监听到,符合我的认知!事件监听肯定需要先注册监听事件,不然事件发出去了,监听的事件都还没注册,神仙也收不到消息啊!nice~ 好的,再来一遍~ 看下打印的结果

image.png

nice~很奇妙~竟然监听到了~还是在created钩子之前就监听到了!阿西,我去你个老六,你组件销毁都不关闭事件监听,你想搞什么?搞爆内存吗?不关闭监听,eventbus注册的事件监听当然还在eventbus的监听事件列表里躺着呢,你再触发事件它当然会监听到啦。你个老六,我还以为是什么神秘代码呢! 不信,你不要刷新浏览器,多来几遍你看打印几次监听到喽!当然是注册几次监听事件就打印几次喽! 而且是一触发事件之后就立即调用了这个监听事件的回调,哪里会等什么组件created、mounted啊,就算你在监听事件里引用了this,那这个this也是形成了闭包了好吗?根本不是你想要的当前那个组件实例那个this,你还指望它能帮你做响应式,想太多了喂!

image.png

那下面就先在组件销毁的时候,关闭事件监听,内存说,我求求了!

beforeDestroy(){
    this.$eventBus.$on('emit-event')
    //管你注册了多少,全给你关了,我真的拴Q!
}

好了,我们再来看下结果

image.png

很好,不管怎么跳,都监听不到事件啦,太棒了,干掉你了!

什么?你就想让它能监听到?你没事吧?

那只能动用重武器了,setTimeout你来吧,你好使,你厉害,因为你是宏任务啊! 不清楚宏任务是什么?嗯?那不赶紧麻溜的去看js事件循环机制啊! 我们给emit加个setTimeout 0

this.$router.push('detail')
setTimeout(() => {
    this.$eventBus.$emit('emit-msg')
}, 0)

再打印下结果 🤔 第一遍

image.png

没监听到,没理由啊,🤔 再跳一次路由

image.png

这一次,是监听到了🤔 这就有点意思了

难道路由这里有什么玄机?? 我们看一下代码执行栈,我们在setTimeout和$on回调里都打上debugger,再找到router push打上debugger,第一遍:首先进入push,push这里返回了一个promise,但注意这里只是返回了一个promise(而不是执行了一个promise.then或者.reject),它还是一个同步任务,直到有函数执行了这里的resolve函数或者reject,才会触发then或者reject,才是一个微任务。

image.png

然后就执行到了setTimeout,压入异步队列。再执行到了confirmTransition

image.png

看看confirmTransition这个函数做了什么,调用confirmTransition函数之前vue-router又做了什么?

image.png

执行到

image.png

image.png

这个函数很长,一直到执行这个onComplete才是执行了resolve微任务。但是第一次显然没有执行到onComplete,直到所有的同步任务都执行完了,已经开始了新的一轮事件循环,就执行到了刚才已经在事件循环队列里面的宏任务,执行了“emit”。然后才进入了刚才的路由代码中的微任务,执行了oncomplete。

image.png

很有意思,需要看一下,虽然知道了第一次路由更新的过程。但是其实中间路由做了什么还是不清楚。第一次没有立即执行微任务,可能跟这个runQueue函数有关系。我们再看第二次是不是push之后就立即执行了这个runQueue。趁runQueue函数还新鲜,再debugger一遍,这一次立即就执行了runQueue的onComplete。执行完了之后,执行栈立马就更新了,也就是一次事件循环又结束了,已经开始了新的一轮事件循环了。第二次执行完router的跳转(onComplete)之后,就立即执行了created钩子,注册了监听函数。之后执行了触发emit的setTimeout函数回调,这时触发emit,监听起了作用。

如果要看router究竟在第一次更新和第二次更新干了什么,为什么会有区别,那只能看我下回更新了。上午更不动了~~