• 【保姆级】react17 事件机制源码解析


    写在前面

    react17是一个过渡版本,最大的改动莫过于针对_事件机制的重构_。虽然现在react18版本已经发布了,但对事件机制这部分几乎是没有改动的,因此这里依然可以对17版本的这部分源码作一次解读。

    摘自_react_官网的独白:

    这里把个人觉得较重大的改动框出来了:

    1.将事件委托给根节点而不是document;
    2.让所有的Capture事件与浏览器捕获阶段保持一致;
    3.移除事件池;

    恕我直言,你看到的不一定是真实的

    1.元素上的事件并不是绑定在本身;2.event 并不是元素本身的事件对象;3.整个事件流(捕获、冒泡)过程都是 react 模拟的;接下来先整个面试题开开胃吧,在 react16.x 版本中,如下代码执行,弹窗组件表现如何?

    state={visible:false
    }
    componentDidMount(){document.addEventListener('click',()=>{this.setState({visible:false})})
    }
    handleClick = ()=>{this.setState({visible:true})
    }
    render(){return(<>{this.state.visible && })
    } 
    

    先卖个关子,看完后面内容就自然知道了😁。

    正式开始吧

    事件绑定
    // packages/react-dom/src/client/ReactDOMRoot.js
    function createRootImpl( container: Container, // 项目根节点tag: RootTag,options: void | RootOptions, ) {if (enableEagerRootListeners) {const rootContainerElement =container.nodeType === COMMENT_NODE ? container.parentNode : container;// 注意此函数 监听所有支持的事件listenToAllSupportedEvents(rootContainerElement);}return root;
    } 
    
    // packages/react-dom/src/events/DOMPluginEventSystem.js
    export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {if (enableEagerRootListeners) {// allNativeEvents 这个变量是所有原生事件的集合allNativeEvents.forEach(domEventName => {if (!nonDelegatedEvents.has(domEventName)) {listenToNativeEvent(domEventName,false,((rootContainerElement: any): Element),null,);}listenToNativeEvent(domEventName,true,((rootContainerElement: any): Element),null,);});}
    } 
    

    这里有一个疑问 allNativeEvents 这个变量是怎么赋值的?实际上 packages/react-dom/src/events/DOMPluginEventSystem.js 在这个文件的顶部

    // packages/react-dom/src/events/DOMPluginEventSystem.js
    import * as BeforeInputEventPlugin from './plugins/BeforeInputEventPlugin';
    import * as ChangeEventPlugin from './plugins/ChangeEventPlugin';
    import * as EnterLeaveEventPlugin from './plugins/EnterLeaveEventPlugin';
    import * as SelectEventPlugin from './plugins/SelectEventPlugin';
    import * as SimpleEventPlugin from './plugins/SimpleEventPlugin';
    
    SimpleEventPlugin.registerEvents();
    EnterLeaveEventPlugin.registerEvents();
    ChangeEventPlugin.registerEvents();
    SelectEventPlugin.registerEvents();
    BeforeInputEventPlugin.registerEvents(); 
    

    在引入插件的同时调用了插件对应的事件注册方法,关于插件的内容在后续讲解针对事件源event的处理时再来讨论。

    // packages/react-dom/src/events/EventRegistry.js
    export const allNativeEvents: Set = new Set();
    export function registerDirectEvent( registrationName: string,dependencies: Array, ) {for (let i = 0; i < dependencies.length; i++) {allNativeEvents.add(dependencies[i]);}
    } 
    

    在搞清楚 allNativeEvents 的来源后我们继续往下

    // packages/react-dom/src/events/DOMPluginEventSystem.js
    function addTrappedEventListener(targetContainer: EventTarget,domEventName: DOMEventName,eventSystemFlags: EventSystemFlags,isCapturePhaseListener: boolean,isDeferredListenerForLegacyFBSupport?: boolean,
    ) {
    // 特别注意这个地方 对事件的回调函数作了一次包装 稍后在事件触发阶段再来详细聊聊let listener = createEventListenerWrapperWithPriority(targetContainer,domEventName,eventSystemFlags,);let unsubscribeListener;if (isCapturePhaseListener) {unsubscribeListener = addEventCaptureListener(targetContainer,domEventName,listener,);} else {unsu
  • 相关阅读:
    LeetCode75——Day13
    如何从 FastReport VCL 中将报表导出为PNG格式?
    山东电信E900V21E_S905L3B_UWE5621_线刷固件包
    6.(vue3.x+vite)路由传参query与params区别
    Docker---Docker-compose 安装部署 minio 存储服务
    实现将一张图片中的目标图片抠出来
    docker-compose搭建etcd集群(三节点)
    安卓毕业设计app项目源码基于Uniapp实现的美食餐厅订餐点餐
    STM32单片机C语言模块化编程实战:LED控制详解与示例
    几个学算法的小窍门,太实用了!
  • 原文地址:https://blog.csdn.net/pfourfire/article/details/126951529