资讯专栏INFORMATION COLUMN

【React深入】React事件机制

philadelphia / 2214人阅读

摘要:给注册原生事件回调为统一的事件分发机制。根据元素唯一标识和事件类型从中取出回调函数返回带有合成事件参数的回调函数总流程将上面的四个流程串联起来。可见,回调函数是直接调用调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的是。

关于React事件的疑问

1.为什么要手动绑定this

2.React事件和原生事件有什么区别

3.React事件和原生事件的执行顺序,可以混用吗

4.React事件如何解决跨浏览器兼容

5.什么是合成事件

下面是我阅读过源码后,将所有的执行流程总结出来的流程图,不会贴代码,如果你想阅读代码看看具体是如何实现的,可以根据流程图去源码里寻找。

事件注册

组件装载 / 更新。

通过lastPropsnextProps判断是否新增、删除事件分别调用事件注册、卸载方法。

调用EventPluginHubenqueuePutListener进行事件存储

获取document对象。

根据事件名称(如onClickonCaptureClick)判断是进行冒泡还是捕获。

判断是否存在addEventListener方法,否则使用attachEvent(兼容IE)。

document注册原生事件回调为dispatchEvent(统一的事件分发机制)。

事件存储

EventPluginHub负责管理React合成事件的callback,它将callback存储在listenerBank中,另外还存储了负责合成事件的Plugin

EventPluginHubputListener方法是向存储容器中增加一个listener。

获取绑定事件的元素的唯一标识key

callback根据事件类型,元素的唯一标识key存储在listenerBank中。

listenerBank的结构是:listenerBank[registrationName][key]

例如:

{
    onClick:{
        nodeid1:()=>{...}
        nodeid2:()=>{...}
    },
    onChange:{
        nodeid3:()=>{...}
        nodeid4:()=>{...}
    }
}
事件触发 / 执行

这里的事件执行利用了React的批处理机制,在前一篇的【React深入】setState执行机制中已经分析过,这里不再多加分析。

触发document注册原生事件的回调dispatchEvent

获取到触发这个事件最深一级的元素

例如下面的代码:首先会获取到this.child

      
this.parent = ref}>
this.child = ref}> test

遍历这个元素的所有父元素,依次对每一级元素进行处理。

构造合成事件。

将每一级的合成事件存储在eventQueue事件队列中。

遍历eventQueue

通过isPropagationStopped判断当前事件是否执行了阻止冒泡方法。

如果阻止了冒泡,停止遍历,否则通过executeDispatch执行合成事件。

释放处理完成的事件。

react在自己的合成事件中重写了stopPropagation方法,将isPropagationStopped设置为true,然后在遍历每一级事件的过程中根据此遍历判断是否继续执行。这就是react自己实现的冒泡机制。

合成事件

调用EventPluginHubextractEvents方法。

循环所有类型的EventPlugin(用来处理不同事件的工具方法)。

在每个EventPlugin中根据不同的事件类型,返回不同的事件池。

在事件池中取出合成事件,如果事件池是空的,那么创建一个新的。

根据元素nodeid(唯一标识key)和事件类型从listenerBink中取出回调函数

返回带有合成事件参数的回调函数

总流程

将上面的四个流程串联起来。

为什么要手动绑定this

通过事件触发过程的分析,dispatchEvent调用了invokeGuardedCallback方法。

function invokeGuardedCallback(name, func, a) {
  try {
    func(a);
  } catch (x) {
    if (caughtError === null) {
      caughtError = x;
    }
  }
}

可见,回调函数是直接调用调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的thisundefined

这里可以使用实验性的属性初始化语法 ,也就是直接在组件声明箭头函数。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。因此这样我们在React事件中获取到的就是组件本身了。

和原生事件有什么区别

React 事件使用驼峰命名,而不是全部小写。

通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。

例如,HTML

React 中略有不同:

另一个区别是,在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault

由上面执行机制我们可以得出:React自己实现了一套事件机制,自己模拟了事件冒泡和捕获的过程,采用了事件代理,批量更新等方法,并且抹平了各个浏览器的兼容性问题。

React事件和原生事件的执行顺序
  componentDidMount() {
    this.parent.addEventListener("click", (e) => {
      console.log("dom parent");
    })
    this.child.addEventListener("click", (e) => {
      console.log("dom child");
    })
    document.addEventListener("click", (e) => {
      console.log("document");
    })
  }

  childClick = (e) => {
    console.log("react child");
  }

  parentClick = (e) => {
    console.log("react parent");
  }

  render() {
    return (
      
this.parent = ref}>
this.child = ref}> test
) }

执行结果:

由上面的流程我们可以理解:

react的所有事件都挂载在document

当真实dom触发后冒泡到document后才会对react事件进行处理

所以原生的事件会先执行

然后执行react合成事件

最后执行真正在document上挂载的事件

react事件和原生事件可以混用吗?

react事件和原生事件最好不要混用。

原生事件中如果执行了stopPropagation方法,则会导致其他react事件失效。因为所有元素的事件将无法冒泡到document上。

由上面的执行机制不难得出,所有的react事件都将无法被注册。

合成事件、浏览器兼容
  function handleClick(e) {
    e.preventDefault();
    console.log("The link was clicked.");
  }
这里, e 是一个合成的事件。 React 根据 W3C 规范 定义了这个合成事件,所以你不需要担心跨浏览器的兼容性问题。

事件处理程序将传递 SyntheticEvent 的实例,这是一个跨浏览器原生事件包装器。 它具有与浏览器原生事件相同的接口,包括 stopPropagation()preventDefault() ,在所有浏览器中他们工作方式都相同。

每个 SyntheticEvent 对象都具有以下属性:

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

React合成的SyntheticEvent采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。

另外,不管在什么浏览器环境下,浏览器会将该事件类型统一创建为合成事件,从而达到了浏览器兼容的目的。

推荐阅读

【React深入】setState的执行机制

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102379.html

相关文章

  • 深入React知识点整理(一)

    摘要:以我自己的理解,函数式编程就是以函数为中心,将大段过程拆成一个个函数,组合嵌套使用。越来越多的迹象表明,函数式编程已经不再是学术界的最爱,开始大踏步地在业界投入实用。也许继面向对象编程之后,函数式编程会成为下一个编程的主流范式。 使用React也满一年了,从刚刚会使用到逐渐探究其底层实现,以便学习几招奇技淫巧从而在自己的代码中使用,写出高效的代码。下面整理一些知识点,算是React看书...

    Gilbertat 评论0 收藏0
  • 谈谈React事件机制和未来(react-events)

    摘要:另外第三方也可以通过的事件插件机制来合成自定义事件,尽管很少人这么做。抽象跨平台事件机制。打算干预事件的分发。事件是的一个自定义事件,旨在规范化表单元素的变动事件。 showImg(https://segmentfault.com/img/remote/1460000019961124?w=713&h=307); 当我们在组件上设置事件处理器时,React并不会在该DOM元素上直接绑定...

    TNFE 评论0 收藏0
  • React事件机制

    摘要:注册事件的回调函数由来统一管理,根据事件的类型和组件标识为唯一标识事件并进行存储。利用中注入的例如会将原生的事件转化成合成的事件,然后批量执行存储的回调函,回调函数的执行分为两步,第一步是将所有的合成事件放到事件队列里面,第二步是逐个执行。   最近在阅读《深入React技术栈》一书中,发现了之前使用React中并没有注意到的React事件与浏览器原生事件之间的区别,鉴于好久已经没有写...

    lavnFan 评论0 收藏0
  • React深入】setState的执行机制

    摘要:调用事务的方法,遍历待更新组件队列依次执行更新。执行生命周期,根据返回值判断是否要继续更新。三总结钩子函数和合成事件中在的生命周期和合成事件中,仍然处于他的更新机制中,这时为。这时将执行之前累积的。 一.几个开发中经常会遇到的问题 以下几个问题是我们在实际开发中经常会遇到的场景,下面用几个简单的示例代码来还原一下。 1.setState是同步还是异步的,为什么有的时候不能立即拿到更新结...

    zombieda 评论0 收藏0
  • 深入react技术栈》学习笔记(三)漫谈React

    摘要:前言接下来让我们进入新的章节漫谈。正文一事件系统的事件系统事件系统符合标准,不存在任何兼容性问题,并且与原生的浏览器事件一样有同样的接口。所有的事件都自动绑定到最外层。组织事件冒泡的行为只适用于合成系统中,且没办法阻止原生事件冒泡。 前言 接下来让我们进入新的章节:漫谈React。本篇文章主要讲React事件系统和表单操作。 正文 一:事件系统 1.react的事件系统react事件系...

    isLishude 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<