浅谈React事件机制

37次阅读

共计 2358 个字符,预计需要花费 6 分钟才能阅读完成。

React 事件简介

React 事件是 合成事件 ,所有事件都自动绑定到最外层上。因为Virtual DOM 在内存中是以 对象 的形式存在的,所以 React 基于 Virtual DOM 实现了一个 SyntheticEvent (合成事件)层,我们所定义的事件 处理器会接收到一个 SyntheticEvent 对象的实例。支持事件的冒泡机制,我 们可以使用 stopPropagation() 和 preventDefault() 来中断它。没有兼容问题。

事件绑定?

事件绑定的方式与原生事件绑定相似,但是必须使用 驼峰的 形式来书写事件的属性名(比如 onClick);
React 并不会像 DOM0 级事件那样将事件处理器直接绑定到 HTML 元素之上,而是一个函数指针:

button onclick="handleClick()">Test</button> //dom0
<button onClick={this.handleClick}>Test</button>

合成事件的实现机制

在 React 底层,主要对合成事件做了两件事:事件委派和自动绑定

1. 事件委派

React 并不会把 事件处理函数 直接绑定到 真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是 在这个统一的事件监听器上插入或删除一些对象; 当事件发生时,首先被这个统一的事件监听器 处理,然后在映射里找到真正的事件处理函数并调用。这样做简化了事件处理和回收机制,效率 也有很大提升。

2. 自动绑定

在 React 组件中,每个方法的上下文都会指向该组件的实例,即自动绑定 this 为当前组件。但是使用 ES6 classes 或者 纯函数 时,需要手动实现 this 的绑定。具体有如下方法:

1》通过 bind 方法实现;每次重新渲染时,都会生成一个新的函数实例保存在 listenerBank 中,浪费内存

render() {
    // 通过 bind 方法实现,可以传递参数
    return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>;
}
render() {// 不传递参数时,也可以使用:: 代替
    return <button onClick={::this.handleClick}>Test</button>;
}

2》构造器内使用 bind 绑定;不用每次调用都绑定。推荐

    import React, {Component} from 'react';
    class App extends Component {constructor(props) {super(props);
        this.handleClick = this.handleClick.bind(this); }
        handleClick(e) {console.log(e);
    }
    render() {return <button onClick={this.handleClick}>Test</button>;
    } }

3》箭头函数

class App extends Component { // 不能带参数
    const handleClick = (e) => {console.log(e);
    };
    render() {return <button onClick={this.handleClick}>Test</button>;
    } 
}
class App extends Component { // 也是每次渲染都会生成新的函数实例
    handleClick(e) {console.log(e); 
    }
  render() {return <button onClick={() => this.handleClick()}>Test</button> 
    }
}

对比 React 合成事件与 JavaScript 原生事件

对于无法使用 React 合成事件的场景,我们还需要使用原生事件来完成。从 4 个方面来对比 React 合成事件与 JavaScript 原生事件。
1. 事件传播与阻止事件传播
浏览器原生 DOM 事件的传播可以分为 3 个阶段:事件捕获阶段 目标对象本身的事件处理程序调用 以及 事件冒泡 。将 e.addEventListener() 的第三 个参数设置为 true 时,可以为事件注册捕获阶段,但是 ie9 一下不支持,所以没什么意义。react 只提供了事件冒泡机制,通过e.prevent- Default() 即可。阻止原生事件传播需要使用 e.preventDefault(),不过对于不支持该方法的浏览器(IE9 以 下),只能使用 e.cancelBubble = true 来阻止。
2. 事件类型
React 合成事件的事件类型是 JavaScript 原生事件类型的一个子集。
3. 事件绑定方式
受到 DOM 标准的影响,绑定浏览器 原生事件 的方式也有很多种,具体如下所示。
1》直接在 DOM 元素中绑定:

<button onclick="alert(1);">Test</button>

2》在 JavaScript 中,通过为元素的事件属性赋值的方式实现绑定:

el.onclick = e => {console.log(e); }

3》通过事件监听函数来实现绑定:

el.addEventListener('click', () => {}, false);
el.attachEvent('onclick', () => {});

相比而言,React 合成事件的绑定方式 则简单得多:

<button onClick={this.handleClick}>Test</button>

4. 事件对象
原生 DOM 事件对象在 W3C 标准和 IE 标准下存在着差异。在低版本的 IE 浏览器中,只能
使用 window.event 来获取事件对象。而在 React 合成事件系统中,不存在这种兼容性问题,在事
件处理函数中可以得到一个合成事件对象。

正文完
 0