事件
事件是javascript与浏览器交互的主要途径。事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示在该对象生命周期中某个有趣的时候到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码并响应。
观察者模式
观察者模式有两类对象组成,主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即时观察者不存在。从另一方面来说,观察者知道主体并能注册事件的回调函数。当涉及DOM时,DOM便是主体,你的事件处理代码便是观察者。
自定义事件
概念
我们知道了事件的基本概念,便得知了自定义事件要符合的条件:概念是创建一个管理事件的对象,让其他对象监听那些事件
代码实现
EventBus.js
function EventTarget() {
// handlers用于储存事件处理程序
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
/**
* @name: addHandler用于注册给定事件的事件处理程序
* @msg: 当调用该方法发的时候,会进行一次检查,看看handlers属性中是否已经存在一个该事件类型的数组\
* 如果没有,则会创建一个新的数组,然后使用push将该处理程序添加到数组的末尾
* @param {*} type 事件类型
* @param {*} handler 处理该事件的函数
*/
addHandler: function (type, handler) {
if (typeof this.handlers[type] === "undefined") {
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
/**
* @name: fire 用于触发一个事件
* @msg: 先给event对象设置一个target属性,如果它尚未指定的话
* 然后查找对应事件类的一组处理程序,调用各个函数,并给出event对象
* @param {*} event 至少包含type的属性对象
*/
fire: function (event) {
if (!event.target) {
event.target = this;
}
if (this.handlers[event.type] instanceof Array) {
var handlers = this.handlers[event.type];
for (let i = 0, len = handlers.length; i < len; i++) {
handlers[i](event)
}
}
},
/**
* @name: removeHandler 是addHandler方法的辅助,它们接受的参数一样
* @msg:搜索事件处理程序的数组找到要删除的处理程序的位置,然后删除
* @param {*} type 事件类型
* @param {*} handler 处理事件的程序
*/
removeHandler: function (type, handler) {
if (this.handlers[type] instanceof Array) {
var handlers = this.handlers[type];
for (var i = 0, len = handlers.length; i < len; i++) {
if (handlers[i] === handler)
break;
}
handlers.splice(i, 1)
}
}
}
export default new EventTarget();
- 以上文件中我们实现了
EventTarget,对应方法addHandler注册/订阅事件,fire触发事件,removeHandler删除事件
React中实现
文件目录
|-- App.js # 根组件
|-- pages
|-----|-- login.js # 登录页面
|-- EventBus
|-----|-- index.js # 事件总线文件
...
其他文件不做赘述
...
测试方法
为了方便贴代码,我在login.js页面中写了两个测试组件,组件传值不会通过Login中转,而是通过组件A订阅事件,组件B触发之间的方式进行传值
login.js
import React from 'react';
import EventBus from "../EventBus";
export default () => {
return (
<React.Fragment>
<div>
登录模块
<Com1 />
<Com2 />
</div>
</React.Fragment>
)
}
function Com1() {
// 触发事件
function emit() {
EventBus.fire({
type: "login",
data: { code: "200", massage: "登录成功" }
})
}
return (
<React.Fragment>
<div>COM1</div>
<button onClick={emit}>com1触发</button>
</React.Fragment>
)
}
function Com2() {
React.useEffect(() => {
// 注册/订阅事件
EventBus.addHandler("login", (event) => {
console.log(event.data, "[COM2模块接受]")
})
return () => EventBus.removeHandler('login', null)
}, []);
return (
<React.Fragment>
<div>
COM2
</div>
</React.Fragment>
)
}
- 如上,我在
Com2组件挂载后订阅了login事件,Com1中通过点击事件触发了login事件,我们来看下效果截图吧。
- 我们在
React中实现了自定义事件总线方式传值
Vue中实现
文件目录
|-- App.vue # 根组件
|-- component
|-----|-- Sidebar.vue #侧边栏组件
|-----|-- Header.vue #页面头部组件
|-- EventBus
|-----|-- index.js # 事件总线文件
...
其他文件不做赘述
...
测试方法
我将通过点击
Header组件中的按钮A对SideBar组件的收起/展开功能来测试自定义事件在Vue中的实现
// 只贴相关代码
Sidebar.vue
created() {
// 组件实例创建之后注册/订阅 collapse事件
EventBus.addHandler('collapse', (event) => {
this.collapse = event.data;
});
}
Header.vue
// 侧边栏折叠 按钮A的点击事件
collapseChage() {
this.collapse = !this.collapse;
EventBus.fire({ type: 'collapse', data: this.collapse });
},
- 如上,我在
SideBar实例创建后注册订阅collapse事件,在组件Header组件中点按钮A点击事件中触发了collapse事件
- 我们在
Vue中实现了自定义事件总线方式传值
概念性文字参考了《JavaScript高级程序设计》
以上