duuliy

你不知道的react事件机制

2020-8-26

react16事件机制,浅提,因为网上很多文章了。

1.原生事件阻止冒泡肯定会组织合成事件的触发,合成事件的阻止冒泡不会影响原生事件。

2.React 将事件全部统一交给 document 来委托处理。

3.onclick={test},test=e=>console.log(e)
这个e是合成的,原生的在e.nativeEvent里面

4.绑定在document 上之后,dispatchEvent 方法统一进行派发(得到事件类型和组件id,在listenerBank里面寻找绑定的回调函数,然后触发)。

与react17的区别(官网也说的很清楚了)

1.事件:16绑定在document(最顶部)上面,17绑定在调整将顶层事件绑在container上,ReactDOM.render(app, container);(createPortal创建的modal也是#Modal-root,也会有一层事件监听),能够解决我们遇到的多版本共存问题,对微前端方案是个重大利好。

2.React 17 中终于支持了原生捕获事件的支持, 对齐了浏览器原生标准。
同时onScroll 事件不再进行事件冒泡

3.移除时间池

重点来了,如何验证

1.首先在.html文件
a.button上绑定事件,如图
图片
b.在document上监听addEventListener,如图
图片

长这样,没有问题,记住!

2.然后在react16 和react17 上的div上绑定事件

图片
图片

16在document,17在root没有问题,但是…
奇怪了!为啥li 和button上都有事件呢,不是说react事件都统一委托到root上吗,难道官网有错?
是不是跟环境有关,行!我去coderunner上写一个,如图
图片
真的怪了,也是在dom上…

推断:
  但是每个子组件的事件却在同一行,又都叫’uc’,说明绑定的是同一个事件,目的是冒泡传递节点信息,而不是具体绑定的事件!!!

查资料,查查查,1个多小时了,翻墙也找不到为什么?
得了,看看源码吧,验证一下推断。

用了ensureListeningTo 做了事件委托,但是这三个为什么不做处理呢?

case TOP_CLOSE:
          if (isEventSupported(getRawEventName(dependency))) {
            trapCapturedEvent(dependency, mountAt);
          }
          break;
        case TOP_INVALID:
        case TOP_SUBMIT:
        case TOP_RESET:
          // We listen to them on the target DOM elements.
          // Some of them bubble so we don't want them to fire twice.
          break;

ensureListeningTo里面:
  trapCapturedEvent 表示捕获阶段的事件监听器
  trapBubbledEvent 表示冒泡阶段的事件监听器

恍然大悟,
原来:事件分为 3 类。分别是 DiscreteEvent(离散事件),UserBlockingEvent(用户阻塞事件),ContinuousEvent(连续事件)。不同类型的事件代表了不同的优先级。
但是:事件委托需要区分捕获和冒泡,有些事件由于没有冒泡过程,只能在捕获阶段进行事件委托。eg:scroll ,focus
还有:没有进行委托的事件是 Form 事件和 Media 事件,原因是这些事件委托后会触发两次回调函数。

不对不对,依然不能验证推断,也解释不通啊?
等等,在线编码打印得事件叫 ’noop‘,有点奇怪,查一下 https://developer.aliyun.com/mirror/npm/package/react-props-noop

https://zhuanlan.zhihu.com/p/68477600 尤大大得 vue3.0也在用 ’noop‘
出结论了:dom上依然有事件函数,但是函数的noop,什么都不做的函数,用于直接返回参数本身

哈哈,开心!强迫症出结论了 我的推断正确!!!