* 确认上课的环境 * 自我介绍 * 课程内容 EventEmitter EventEmitter是Node.js的内置模块events提供的一个类,它是Node事件流的核心,EventEmitter是服务端的东西, 前端已经有event-emitter的npm库 地址: https://www.npmjs.com/package/event-emitter 高级浏览器也有原生提供的EventTarget这种实现事件监听和触发的API 地址: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget 但是它们和Node.js的事件API都有或多或少的区别,今天我们就来实现一个前端版本的EventEmitter 我本章demo的github地址如下 https://github.com/penghuwan/event-emitter
* 引导学员加微信
1. 咱们为什么要学这堂课? * 了解观察者模式和 发布订阅模式 的区别 * 不要一味的使用别人的插件, 自己可以动手造*吗, 自己能开发插件供别人学习吗?
2. 封装完这个 EventEmitter有什么作用? 如何运用到项目中去? * 可以简单演示下在项目中的使用?
## 观察者和发布订阅模式的区别
## 项目中常见的发布订阅模式
* 父子组件传值 子组件 v-on:eventKK - 父组件 this.$emit('eventKK') * *数据总线 ```javascript var bus = new Vue() // 触发组件 A 中的事件 bus.$emit('id-selected', 1) // 在组件 B 创建的钩子中监听事件 bus.$on('id-selected', function (id) { // ... }) ``` * vuex状态管理
### 二、API介绍 我们要实现的API有:
* on(event, listener):为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。 * emit(event, [arg1], [arg2]): 按监听器的顺序执行执行每个监听器 * addListener(event, listener):on的同名函数(alias) * once(event, listener): 和on类似,但只触发一次,随后便解除事件监听 * removeListener(event, listener): 移除指定事件的某个监听回调 * removeAllListeners([event]):移除指定事件的所有监听回调 * setMaxListeners(n):用于提高监听器的默认限制的数量。(默认10监听回调个产生警告)
![avatar](./pic/event.png)
eventEmitter具体代码
1. 首先我们需要写一个EventEmitter构造函数,给它设置两个属性listeners和maxListener
```javascript // 采用发布订阅模式 class EventEmitter { constructor(max){ this.listeners = {}; //用于存放事件监听器函数,结构为: // { // "event1": [f1,f2,f3], // "event2": [f4,f5], // ... // } this.maxListener = max || 10; //设置的某个事件能够添加的监听器的最大数量,超过这个值,需要在控制台输出警告,但不会报错阻止
} //监听事件 // 1.判断该事件的监听器数量是否已超限,超限则报警告 // 2.判断该事件监听器数组是否初始化,若未初始化,则将listeners[event]初始化为数组,并加入监听器cb // 3.若监听器数组已经被初始化,则判断数组中是否已存在cb,不存在则添加,已存在则不做操作。 on(event,cb){ var listener = this.listeners; //step 1 判断该事件的监听器数量是否已超限,超限则报警告 if(listener[event] && listener[event].length>=this.maxListener) { throw console.error('监听器的最大数量是%d,您已超出限制', this.maxListener) } //step 2 判断数组是否已初始化 // Array.isArray(listener[event]) // Object.prototype.toString.call(test) == '[object Array]' if(listener[event] instanceof Array) { //已经初始化,无需重复添加 if(!listener[event].includes(cb)){ listener[event].push(cb); } } else { listener[event] = []; listener[event].push(cb); }
} // 指定addListener等于on方法 addListener(){ this.on.apply(this,arguments); } // (1)通过Array.from(arguments)取出方法的参数列表args, // (2)调用args.shift踢掉数组第一个参数即event,留下来的这些是要传给监听器的 // (3)遍历监听器,通过apply方法把上面得到的args参数传进去 emit(event){ //判断有没有这个event if(!this.listeners[event]){ return; } //因为可能会有参数 var args = Array.from(arguments); args.shift();
//循环遍历所有监听到event的事件 ,统一执行 // this.listeners[event].forEach((cb,index)=>{ // cb.apply(null,args); // }) // 简写为: this.listeners[event].forEach(cb=>cb.apply(null,args)) }
// 1.通过indexOf确定监听器回调在数组listeners[event]中的位置 // 2.通过splice(i,1)删除之 removeListener(event,listener){ //判断有没有这个event var listeners = this.listeners[event]; if(listeners && listeners.length>0){ var i = listeners.indexOf(listener); if (i >= 0) { listeners.splice(i, 1); } } } // 添加事件监听,只能执行一次 // once方法是on方法和removeListener方法的结合: // 用on方法监听,在回调结束的最后位置,通过removeListener删掉监听函数自身 once(){ var self = this; function fn() { var args = Array.prototype.slice.call(arguments); listener.apply(null, args); self.removeListener(event, fn); } this.on(event, fn) } // 清空listeners[event]数组 removeAllListener(){ this.listeners[event] = []; } setMaxListeners(num){ this.maxListener = num; }
} ``` html部分
```javascript <body> <div id="itapp"> <button type="button" id="btnTest">测试</button> </div> <script src="./main.js"></script> <script> var EmitterEx = new EventEmitter(); function fn1(data){ console.log(data) console.log('监听laney'); } function fn2(){ console.log('监听song'); } EmitterEx.on('laney',fn1); EmitterEx.on('song',fn2);
EmitterEx.emit('laney',{ name:'laney', age:'20' }) </script> </body> ```
index.html测试使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>event emiter</title> </head> <body> <div id="itapp"> <button type="button" id="btnTest">测试</button> </div> <script src="./main.js"></script> <script> var EmitterEx = new EventEmitter();
// //测试 最多监听的 总数 setMaxListeners,这个需要在监听之前调用 EmitterEx.setMaxListeners(1);
//测试 on emit function fn1(data){ console.log(data) console.log('监听laney'); } function fn11(data){ console.log(data) console.log('监听laney1'); } function fn2(){ console.log('监听song'); } EmitterEx.addListener('laney',fn1) //监听方式一 // EmitterEx.on('laney',fn1); //监听方式二 EmitterEx.on('song',fn2);
// EmitterEx.on('laney',fn11); // EmitterEx.on('song',fn2);
btnTest.onclick = function(){ EmitterEx.emit('laney',{ name:'laney', age:'20' }) EmitterEx.emit('song',{ name:'song', age:'1' }) }
// //测试 removeListener // EmitterEx.removeListener('laney',fn1)
// //测试 removeAllListener // EmitterEx.removeAllListener();
</script> </body> </html>