自定义事件
事件是JavaScript与浏览器交互的主要途径。事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示该对象生命周期某个有趣的时刻要到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码来响应。
观察者由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。从另一方面说,观察者知道主体并能注册事件的回调函数。涉及DOM上时,DOM元素便是主体,你的事件处理代码便是观察者。
事件是与DOM交互的最常见方式,但它们也可以用于非DOM代码中——通过实现自定义事件。自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。实现此功能的基本模式如下:
- 自定义事件是什么
- 自定义事件实现原理
- 自定义事件应用
首先我们写一个最简单的自定义事件,这样有利于让我们理解自定义事件的基本原理。
ES5写法:
function EventTarget(){
this.handlers = {}
}
EventTarget.prototype = {
//添加一个事件
addHandler: function(type,handler){
this.handlers[type] = handler;
},
//触发一个事件
fire: function(type){
this.handlers[type]();
},
//移除一个事件
removeHandler: function(type,handler){
this.handlers[type] = null;
}
}
//创建一个新的对象
var target = new EventTarget();
function messageHandler(){
console.log('hello,world!');
}
//添加一个事件处理程序
target.addHandler('message',messageHandler);
//触发一个事件
target.fire('message');
//移除一个事件处理程序
target.removeHandler('message',messageHandler);
//再次没有应用程序
target.fire('message');
ES6写法:
Class EventTarget{
......
}
如果添加很多次事件处理程序的话会如何呢?
结果是后面把前面的覆盖了。因此在这里我们应该用数组管理。
function EventTarget(){
this.handlers = {}
}
EventTarget.prototype = {
//添加一个事件
addHandler: function(type,handler){
if(typeof this.handlers[type] == 'undefined'){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
//触发一个事件
fire: function(event){
var handlers = this.handlers[event.type];
//这里为何要判断?
if(handlers instanceof Array){
for(var i=0;i<handlers.length;i++){
handlers[i](event.message);
}
}
},
//移除一个事件
removeHandler: function(type,handler){
// this.handlers[type] = null;
var handlers = this.handlers[type];
if(handlers instanceof Array){
for(var i=0;i<handlers.length;i++){
if(handlers[i] == handler){
break;
}
}
handlers.splice(i,1);
}
}
}
var target = new EventTarget();
function messageHandler(message){
console.log(message);
}
function messageHandler2(message){
console.log(message);
}
target.addHandler('message',messageHandler);
target.addHandler('message',messageHandler2);
target.fire({type:'message',message:'Hello,World!'});
target.removeHandler('message',messageHandler);
target.fire({type:'message',message:'Hello,World2!'});
自定义事件应用
上面是自定义事件的实现原理,那么它的应用场景是上面呢?
通常我们在React中兄弟之间传值最基本的方法是:兄弟A传给父组件,然后通过父组件传给兄弟B组件。
父组件
import React,{ Component } from 'react';
import BrotherA from './BrotherA';
import BrotherB from './BrotherB';
class App extends Component {
constructor(props) {
super(props);
}
getStrFun(str){
this.setState({
str,
})
}
render() {
return (
<div>
<RouteMap />
<Acom />
<Ccom />
<BrotherA information={ (str)=>this.getStrFun(str)}/>
<BrotherB str={ this.state.str }/>
</div>
)
}
}
export default App;
兄弟组件A
import React,{ Component } from 'react';
import { Button } from 'antd';
class BrotherA extends Component{
constructor(props){
super(props);
this.state = {
str: '兄弟A',
}
}
componentDidMount(){
}
clickHandle(){
console.log('dianji');
this.props.information && this.props.information(this.state.str);
}
render(){
return(
<div>
<Button type='primary' onClick={()=>{ this.clickHandle()}}>
BrotherA
</Button>
</div>
)
}
}
export default BrotherA;
兄弟组件B
import React,{ Component } from 'react';
class Brother extends Component{
constructor(props){
super(props);
}
render(){
return(
<div>
BrotherB,{ this.props.str }
</div>
)
}
}
export default Brother;
对于箭头函数的理解,在这里我多写几句。
clickHandle(){
console.log('dianji');
this.props.information && this.props.information(this.state.str);
}
<BrotherA information={ (str)=>this.getStrFun(str)}/>
this.props.information(this.state.str)其实相当于执行了函数,然后把参数传给getStrFun(str)函数。
这种方法传值存在一个问题,当嵌套比较深的时候,一级一级向上传,特别的麻烦,并不是一个理想的方式。
这时候有两种方式可以解决:
- redux
- 自定义事件
在这里我们只介绍自定义事件。
兄弟A组件(BrotherA)
import React,{ Component } from 'react';
import { Button } from 'antd';
import target from './Utils';
class BrotherA extends Component{
constructor(props){
super(props);
this.state = {
str: '兄弟A',
}
}
componentDidMount(){
}
handle(){
target.fire({type:'message',message:'Hello,World!'});
}
render(){
return(
<div>
<Button type='primary' onClick={ ()=> this.handle()}>
传值给B
</Button>
</div>
)
}
}
export default BrotherA;
兄弟B组件(BrotherB)
import React,{ Component } from 'react';
import target from './Utils';
class Brother extends Component{
constructor(props){
super(props);
this.state = {
str:'',
}
}
componentDidMount(){
// target.addHandler('message',(str)=>{
// this.setState({
// str,
// })
// })
//这里为何不能这样写呢?
var that = this;
target.addHandler('message',function(str){
that.setState({
str,
})
})
}
render(){
return(
<div>
<h2>得到兄弟A,{ this.state.str }</h2>
</div>
)
}
}
export default Brother;
经过实践证明BrotherA的componentMount比BrotherB组件执行快几毫秒。点击事件是相对滞后。