React事件系统整理

事件系统

Virtual DOM在内存中是以对象的形式存在,如果想要在这些对象上加事件就会比较简单。React基于Virtual DOM实现了一个合成事件层,我们所定义的事件会接受到一个合成事件对象的实例。不会存在IE浏览器兼容性的问题,同样支持事件冒泡机制。

合成事件绑定方式

React事件的绑定方式在写法上与原生HTML事件监听很相似。

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

这个和JavaScript的DOM0级事件很像,但是又有一些不同:HTML的事件需要全部小写的属性名,而且jsx的属性值可以是任何类型。这里是一个函数指针。

<button onclick="handle()"></button>

React并不会像DOM0级事件那样将事件处理器直接绑定到DOM上,React仅仅是借鉴了这种写法。

合成事件的实现机制

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

1. 事件委派

React中并不是把事件处理函数绑定到当前DOM上,而是把所有的事件绑定到结构的最外层,使用统一的事件监听器。
这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。
组件挂载和卸载时,只是在统一事件监听器上插入删除一些对象。

2. 自动绑定

在React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前的组件。而且React会对这种引用缓存,以达到CPU和内存的最大优化。
注意:在使用es6 class或者纯函数的写法,这种绑定不复存在,我们需要手动实现this绑定。

  • bind方法:该方法可以帮助我们绑定事件处理器内的this,并可以向事件处理器中传入参数:
class App extends Component {
    handleClick(e,arg) {
        console.log(e,arg)
    }
    render() {
        return <button onClick={this.handleClick.bind(this,'test')}></button>
    }
}

如果只绑定方法,不传递参数,stage0提供一个双冒号语法:

class App extends Component {
    handleClick(e) {
        console.log(e)
    }
    
    render() {
        return (
            <button onClick{::this.handleClick}></button>
        )
    }
}
  • 构造器内声明:在组件的构造器内完成this的绑定,这种绑定的好处在于仅仅需要进行一次绑定,而不需要每次调用事件监听器的时候去执行绑定操作。
import React,{Component} from 'react'

class App extends Component {
    constructor(props) {
        super(props)
    }
    handleClick(e) {
        console.log(e)
    }
    render() {
        return (
            <button onClick={this.handleClick}></button>
        )
    }
}
  • 箭头函数:箭头函数自动绑定了定义此函数作用域的this,因此我们不需要再对它使用bind方法。
class App extends Component {
    const handleClick = (e) => {
        console.log(e)
    }
    
    render() {
        return (
            <button onClick={this.handleClick}></button>
        )
    }
}

如上集中方法,都能实现在类定义的组件中绑定this上下文的效果。

在React中使用原生事件

我们可以在componentDidMount方法中完成原生事件的绑定,此时组件已经安装完成并且在浏览器中存在真实的dom。

class App extends Component {
    componentDidMount() {
        this.refs.button.addEventListener('click',e => {
            this.handleClick()
        })
    }
    
    handleClick(e) {
        console.log(e)
    }
    
    componentWillUnmount() {
        this.refs.button.removeEventListener('click')
    }
    render() {
        return <button ref='button'></button>
    }
}

在React中使用DOM原生事件时,一定要在组件卸载时手动移除,否则很有可能出现内存泄漏的问题。然而合成事件在内部已经很好的帮我们处理过了。

合成事件与原生事件混用

reactEvent.nativeEvent.stopPropagation()只能用于React合成事件系统中,没办法阻止原生事件的冒泡。原生事件阻止冒泡行为可以阻止React合成事件的传播。

React合成事件系统只是原生DOM事件系统的一个子集,它仅仅实现了DOM LEVEL3事件接口,并且统一了浏览器的兼容性问题。

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

我们从4个方面来对比React合成事件与JavaScript原生事件。

  • 事件传播与阻止事件传播

浏览器原生DOM事件的传播可以分为3个阶段:事件捕获阶段、目标对象本身事件处理程序、事件冒泡阶段
事件捕获会优先调用结构树最外层的元素上绑定的事件监听器,然后依次向内调用,一直调用到目标元素上的事件监听器为止。可以在e.addEventListener()的第三个参数设置为true时,为元素e注册捕获事件处理程序,并且在事件传播的第一个阶段调用。事件捕获在低于IE9的浏览器中不支持。
事件冒泡和事件捕获相反,它会从目标元素向外传播,由内而外直到最外层。

React合成事件中没有实现事件捕获,仅仅支持事件冒泡机制。

阻止原生事件传播需要使用e.preventDefault(),不过对于不支持的浏览器可以使用e.cancelBubble = true来阻止。React合成事件中使用e.preventDefault()即可。

  • 事件类型

React合成事件的事件类型是JavaScript原生事件类型的子集。

  • 事件绑定方式

原生事件绑定的方式如下:

1. <button onclick="alert(1)">btn</button>
2. e.onclick = function() {}
3. e.addEventListener()

React事件绑定方式比较简单:

<button onClick={this.handle.bind(this)}></button>
  • 事件对象

原生DOM事件对象在不同浏览器中存在着差异。在低版本的IE浏览器中,只能使用window.event来获取事件对象。React中不存在这些差异,在事件处理函数中可以得到一个合成事件对象。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom