2021年1月12日(react合成事件)


1.react为什么要提供合成事件?

官方文档的描述:

SyntheticEvent 实例将被传递给你的事件处理函数,它是浏览器的原生事件的跨浏览器包装器。除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口

可以看出React 想实现一个全浏览器的框架, 为了实现这种目标就需要提供全浏览器一致性的事件系统,以此抹平不同浏览器的差异

2.react的合成事件和原生事件如何对应,如何绑定呢?

合成事件和原生事件的对应关系被存放在EventPlugin中,可以认为是将不同的合成事件函数封装成一个模块,每个模块只处理自己对应的合成事件,方便解耦

React 在一开始就将事件插件全部加载进来, ReactDOMClientInjection

injectEventPluginsByName({
    SimpleEventPlugin: LegacySimpleEventPlugin,
    EnterLeaveEventPlugin: LegacyEnterLeaveEventPlugin,
    ChangeEventPlugin: LegacyChangeEventPlugin,
    SelectEventPlugin: LegacySelectEventPlugin,
    BeforeInputEventPlugin: LegacyBeforeInputEventPlugin
});

第一个对象是 registrationNameModule,实现合成事件到plugin的映射,判断一个组件的prop是否是事件类型,如果再才会被当做事件处理

{
    onBlur: SimpleEventPlugin,
    onClick: SimpleEventPlugin,
    onClickCapture: SimpleEventPlugin,
    onChange: ChangeEventPlugin,
    onChangeCapture: ChangeEventPlugin,
    onMouseEnter: EnterLeaveEventPlugin,
    onMouseLeave: EnterLeaveEventPlugin,
    ...
}

第二个对象是 registrationNameDependencies,这个对象是合成事件到原生事件的映射

{
    onBlur: ['blur'],
    onClick: ['click'],
    onClickCapture: ['click'],
    onChange: ['blur', 'change', 'click', 'focus', 'input', 'keydown', 'keyup', 'selectionchange'],
    onMouseEnter: ['mouseout', 'mouseover'],
    onMouseLeave: ['mouseout', 'mouseover'],
    ...
}

第三个对象是 plugins, 这个对象就是上面注册的所有插件列表

plugins = [LegacySimpleEventPlugin, LegacyEnterLeaveEventPlugin, ...];

1.在React 的协调(Reconciler)阶段,也可以说是diff阶段,标记出哪些 DOM 类型 的节点需要添加或者更新

2.当检测到需要创建一个节点或者更新一个节点时, 使用 registrationNameModule 查看一个 prop 是不是一个事件类型,如果是则执行下一步

3.通过 registrationNameDependencies 检查这个 React 事件依赖了哪些原生事件类型

4.检查这些一个或多个原生事件类型有没有注册过,如果有则忽略

5.如果这个原生事件类型没有注册过,则注册这个原生事件到 document

3.setState是异步还是同步

网上有不少答案是这样说的:在钩子函数和合成事件中是异步,在原生事件和setTimeout中是同步

为什么被setTimeout包裹的this.setState可以在当前调用栈获取到更新后的state

在老版React中,事件回调会被包裹在batchedUpdates函数中执行

function batchedUpdates(fn) {
  let prevContext = context;
  context |= batchedContext;
  try {
    fn();
  } finally {
    context = prevContext;
  }
}

被包裹的事件回调fn通过全局变量context就能获取当前是否处于batchedContext的上下文环境。

如果处于该环境就执行一些批处理操作。

而是否用setTimeout包裹this.setState影响的,就是在执行this.setState时全局变量context是否包含batchedContext

在v17以后,就不会出现这样的问题

4. React 17 中事件系统有哪些新特性
  • 将顶层事件绑定在 container 上而不是 document 上能够解决我们遇到的多版本共存问题

  • React 17 中终于支持了原生捕获事件的支持, 对齐了浏览器原生标准。同时onScroll 事件不再进行事件冒泡。onFocusonBlur使用原生focusinfocusout` 合成。

  • 取消事件复用


Author: wxy
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source wxy !
  TOC