自定义事件

自定义事件
事件是JavaScript与浏览器交互的主要途径。事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示该对象生命周期某个有趣的时刻要到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码来响应。

观察者由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。从另一方面说,观察者知道主体并能注册事件的回调函数。涉及DOM上时,DOM元素便是主体,你的事件处理代码便是观察者。

事件是与DOM交互的最常见方式,但它们也可以用于非DOM代码中——通过实现自定义事件。自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。实现此功能的基本模式如下:

  1. 自定义事件是什么
  2. 自定义事件实现原理
  3. 自定义事件应用

首先我们写一个最简单的自定义事件,这样有利于让我们理解自定义事件的基本原理。
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)函数。

这种方法传值存在一个问题,当嵌套比较深的时候,一级一级向上传,特别的麻烦,并不是一个理想的方式。
这时候有两种方式可以解决:

  1. redux
  2. 自定义事件
    在这里我们只介绍自定义事件。

兄弟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组件执行快几毫秒。点击事件是相对滞后。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值