写在前面:本文为个人在日常工作和学习中的一些总结,便于后来查漏补缺,非权威性资料,请带着自己的思考^-^。
Vue的组件通讯场景
- 父子组件通讯,使用props or 回调函数
- 状态较多、复杂,项目比较繁杂, 使用vuex or mobx 等状态管理工具
- 非父子组件,使用Event Bus
Event Bus使用的简单回顾
在日常开发中有关Event Bus我们是怎么用的呢?
// Vue instance
export default new Vue()
// component A
...
import EventBus from 'Vue instance'
...
methods: {
fn() {}
},
created() {
EventBus.$on('test', fn)
}
...
component B
import EventBus from 'Vue instance'
...
methods: {
handleSomething() {
EventBus.$emit('test', /* some params */)
}
}
...
下面循着这个使用形式,尝试去实现一下
基本功能
Event Bus的基本功能无非是发布订阅模式的一种应用
class Event {
constructor() {
this._events = new Map();
}
$on(type, fn) {
let handlers = [fn]
if (this._events.has(type)) {
handlers = this._events.get(type).concat(handlers)
}
this._events.set(type, handlers)
}
$emit(type, ...args) {
const fns = this._events.get(type)
if (!fns) return
for(let i = 0; i < fns.length; i++) {
fns[i].apply(this, args)
}
}
}
一个简单的发布/订阅类,测试一下
function testFn(a) {
console.log(a)
}
const emitter = new Event()
emitter.$on('test', testFn) // 订阅'test'事件
emitter.$emit('test', 'cont') // 发布'test'事件
结果打印 'cont', 表明已经正常工作
多个监听者呢?
function testFn1(a) {
console.log('1' + a)
}
function testFn2(a) {
console.log('2' + a)
}
emitter.$on('test', testFn1)
emitter.$on('test1', testFn2)
emitter.$emit('test', '1')
emitter.$emit('test1', '2')
结果也可以正常运行
至此,表明该事件类可以支持多个事件监听,也可以支持一个事件多个监听者函数
移除监听者函数
接下来更新一下需求: Event类允许移除之前添加的某一个监听者
class Event{
constructor() {
...
}
...
$off(type, fn) {
if (this._events.has(type)) {
if (!fn) { // 如果没有指定监听者函数,我们认为想要移除所有的type事件的监听者函数
this._events.delete(type)
} else { // 移除事件的fn监听者函数
const handlers = this._events.get(type)
const position = handlers.findIndex(_ => _ === fn)
handlers.splice(position, 1)
this._events.set(type, handlers)
}
}
}
}
之前我们在'test'事件中绑定了2个监听者函数testFn 和 testFn1,现在试着解除一个testFn1
emitter.$off('test', testFn1)
emitter.$emit('test', 'z')
发现现在就只执行了testFn这个监听者函数
注: 匿名函数作为监听者函数是不能被移除的
只监听一次的情况
某些场景,我们希望对某个事件的触发只做出一次响应,此时可以通过包装监听者函数,当监听者函数在执行后便进行$off操作,解绑事件
...
$once(type, fn) {
const me = this
function on(...args) {
me.$off(type, on)
fn.apply(me, args)
}
this.$on(type, on)
}
...
一点问题
在现有代码的基础上如果这样会怎么样呢?
function fn(a) {
console.log(a)
}
emitter.$on('ev', fn)
emitter.$on('ev', fn)
结果是fn函数会被执行2次,也就是说现在同一个事件,相同的监听者函数可以重复进行添加
这是不是问题呢? 视场景而定吧。。。
最终完整代码
class Event {
constructor() {
this._events = new Map();
}
$on(type, fn) {
let handlers = [fn]
if (this._events.has(type)) {
handlers = this._events.get(type).concat(handlers)
}
this._events.set(type, handlers)
}
$off(type, fn) {
if (this._events.has(type)) {
if (!fn) { // 如果没有指定监听者函数,我们认为想要移除所有的type事件的监听者函数
this._events.delete(type)
} else { // 移除事件的fn监听者函数
const handlers = this._events.get(type)
const position = handlers.findIndex(_ => _ === fn)
handlers.splice(position, 1)
this._events.set(type, handlers)
}
}
}
$once(type, fn) {
const me = this
function on(...args) {
me.$off(type, on)
fn.apply(me, args)
}
this.$on(type, on)
}
$emit(type, ...args) {
const fns = this._events.get(type)
if (!fns) return
for(let i = 0; i < fns.length; i++) {
fns[i].apply(this, args)
}
}
}
总结
其实Vue中的Event Bus就是发布/订阅模式的一个应用
THE END
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。