首页 > 其他分享 >react是如何实现事件代理的

react是如何实现事件代理的

时间:2022-10-24 22:12:05浏览次数:55  
标签:NoLanes 优先级 函数 代理 react 事件 null root

_版本:v18.2.0
本文为我花了大半年的时间潜心研究所写,转载请注明出处,谢谢

react是如何实现事件代理的

createRoot函数

** 用户在index.tsx中执行ReactDOM.createRoot创建root节点
1. 通过创建FiberRootNode实例,得到root根节点
2. 执行createFiber(HostRoot, null, null, ConcurrentMode),创建root根节点对应的fiber节点
3. root.current = fiber
4. fiber.stateNode = root
5. container[internalContainerInstanceKey] = root.current 将fiber绑定到dom容器上
6. 执行listenToAllSupportedEvents函数
    1. 遍历allNativeEvents数组,此数组包含所有js事件名
        判断事件名是否在nonDelegatedEvents中,其中nonDelegatedEvents数组保存着不需要冒泡的事件名
        ** 如果在,表示此事件只需要绑定“捕获”
            执行listenToNativeEvent(domEventName, true, rootContainerElement)在rootContainerElement监听代理事件
        ** 如果不在,表示此事件“冒泡”和“捕获”都要执行
            listenToNativeEvent(domEventName, false, rootContainerElement)
            listenToNativeEvent(domEventName, true, rootContainerElement)
    2. selectionchange事件单独处理,需要代理到document上
        listenToNativeEvent('selectionchange', false, document)
7. 创建ReactDOMRoot实例,ReactDOMRoot构造函数的原型上绑定了render和unmount函数,返回此实例

通用函数

listenToNativeEvent函数
1. 捕获阶段:eventSystemFlags |= IS_CAPTURE_PHASE
2. 冒泡阶段:eventSystemFlags = 0
3. 执行addTrappedEventListener函数添加事件监听器
    1. 调用createEventListenerWrapperWithPriority函数获取listenerWrapper
    2. 如果浏览器支持事件passive,则touchstart | touchmove | wheel 事件,开启passive
    3. 如果是捕获事件,开启capture
    4. container.addEventListener(domEventName, listenerWrapper, {...})

createEventListenerWrapperWithPriority函数
1. 调用getEventPriority函数,根据事件名获取事件的优先级
2. 根据事件优先级获取对应的listenerWrapper
    DiscreteEventPriority -> dispatchDiscreteEvent
    ContinuousEventPriority -> dispatchContinuousEvent
    其他 -> dispatchEvent
3. listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer)

getEventPriority函数
根据事件名获取事件的优先级,有如下优先级
** ['cancel', 'click', 'close', 'contextmenu', 'copy', 'cut', 'auxclick', 'dblclick', 'dragend', 'dragstart', 'drop', 
'focusin', 'focusout', 'input', 'invalid', 'keydown', 'keypress', 'keyup', 'mousedown', 'mouseup', 'paste', 'pause', 
'play', 'pointercancel', 'pointerdown', 'pointerup', 'ratechange', 'reset', 'resize', 'seeked', 'submit', 
'touchcancel', 'touchend', 'touchstart', 'volumechange', 'change', 'selectionchange', 'textInput', 'compositionstart', 
'compositionend', 'compositionupdate', 'beforeblur', 'afterblur', 'beforeinput', 'blur', 'fullscreenchange', 'focus', 
'hashchange', 'popstate', 'select', 'selectstart'] 返回 DiscreteEventPriority
** ['drag', 'dragenter', 'dragexit', 'dragleave', 'dragover', 'mousemove', 'mouseout', 'mouseover', 'pointermove', 
'pointerout', 'pointerover', 'scroll', 'toggle', 'touchmove', 'wheel', 'mouseenter', 'mouseleave', 'pointerenter', 
'pointerleave'] 返回 ContinuousEventPriority
** 其他事件,返回DefaultEventPriority
特殊情况,message事件需要根据Scheduler调度包中的currentPriorityLevel优先级,返回对应的事件优先级

附录

root = {
    "tag": 1, // 指定react运行模式 ConcurrentRoot || LegacyRoot
    "containerInfo": {}, // react实例挂载容器
    "pendingChildren": null,
    "current": null,
    "pingCache": null,
    "finishedWork": null,
    "timeoutHandle": -1,
    "context": null,
    "pendingContext": null,
    "callbackNode": null,
    "callbackPriority": 0, // NoLane
    "eventTimes": [ 0, ... ],
    "expirationTimes": [ -1, ... ],
    "pendingLanes": 0, // NoLanes
    "suspendedLanes": 0, // NoLanes
    "pingedLanes": 0, // NoLanes
    "expiredLanes": 0, // NoLanes
    "mutableReadLanes": 0, // NoLanes
    "finishedLanes": 0, // NoLanes
    "entangledLanes": 0, // NoLanes
    "entanglements": [ 0, ... ],
    "identifierPrefix": "", // createRoot的options参数
    "onRecoverableError": reportError, // createRoot的options参数
    "pooledCache": null,
    "pooledCacheLanes": 0, // NoLanes
    "mutableSourceEagerHydrationData": null,
    "effectDuration": 0,
    "passiveEffectDuration": 0,
    "pendingUpdatersLaneMap": [new Set(), ...]
    "_debugRootType": "createRoot()" // 创建此节点的调用方式
}

fiber = {
    "tag": tag, // 节点类型
    "key": key,
    "elementType": null,
    "type": null,
    "stateNode": null,
    "return": null,
    "child": null,
    "sibling": null,
    "index": 0,
    "ref": null,
    "pendingProps": pendingProps,
    "memoizedProps": null,
    "updateQueue": null,
    "memoizedState": null,
    "dependencies": null,
    "mode": mode,
    "flags": NoFlags,
    "subtreeFlags": NoFlags,
    "deletions": null,
    "lanes": NoLanes,
    "childLanes": NoLanes,
    "alternate": null,
    "actualDuration": 0,
    "actualStartTime": -1,
    "selfBaseDuration": 0,
    "treeBaseDuration": 0,
    _debugHookTypes: null,
    _debugNeedsRemount: false,
    _debugOwner: null,
    _debugSource: null,
}

allNativeEvents = [
    'abort', 'auxclick', 'cancel', 'canplay', 'canplaythrough', 'click', 'close', 'contextmenu', 'copy', 'cut', 'drag',
    'dragend', 'dragenter', 'dragexit', 'dragleave', 'dragover', 'dragstart', 'drop', 'durationchange', 'emptied', 
    'encrypted', 'ended', 'error', 'gotpointercapture', 'input', 'invalid', 'keydown', 'keypress', 'keyup', 'load', 
    'loadeddata', 'loadedmetadata', 'loadstart', 'lostpointercapture', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 
    'mouseup', 'paste', 'pause', 'play', 'playing', 'pointercancel', 'pointerdown', 'pointermove', 'pointerout', 
    'pointerover', 'pointerup', 'progress', 'ratechange', 'reset', 'resize', 'seeked', 'seeking', 'stalled', 'submit', 
    'suspend', 'timeupdate', 'touchcancel', 'touchend', 'touchstart', 'volumechange', 'scroll', 'toggle', 'touchmove', 
    'waiting', 'wheel', 'animationend', 'animationiteration', 'animationstart', 'dblclick', 'focusin', 'focusout', 
    'transitionend', 'change', 'selectionchange', 'compositionend', 'textInput', 'compositionstart', 'compositionupdate']

nonDelegatedEvents = ['cancel', 'close', 'invalid', 'load', 'scroll', 'toggle', 'abort', 'canplay', 'canplaythrough', 
    'durationchange', 'emptied', 'encrypted', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 
    'playing', 'progress', 'ratechange', 'resize', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting']

标签:NoLanes,优先级,函数,代理,react,事件,null,root
From: https://www.cnblogs.com/ye-hcj/p/16823195.html

相关文章

  • dellphi Tcxgrid 引用双击事件
      我设置了一个双击事件,然后又想通过右键菜单来实现双击整事件里的功能,就这么一个简单的功能,我卡了一天!主要卡在ACellViewInfo这个参数里面,想传这个参数进去,......
  • react中useTransition的执行原理
    _版本:v18.2.0本文为我花了大半年的时间潜心研究所写,转载请注明出处,谢谢本文假设你读了本系列中的《触发onClick,react会执行哪些操作》、《react中suspense组件是如何重......
  • SpringBoot代理图片、文件等路径
    在config文件夹下新增一个配置类即可 /***@authorcyl*@time2022/10/24*/@ConfigurationpublicclassMyWebAppConfigurationextendsWebMvcConfigurerAda......
  • JS事件循环之宏任务和微任务
    首先我们知道javascript是一个单线程的脚本语言,也就是说我们在执行代码的过程中不会出现同时进行两个进程(执行两段代码)。JS执行过程中会产生两种任务,分别是:同步任务和异......
  • 复合事件处理(Complex Event Processing)介绍
    复合事件是由史丹佛大学的DavidLuckham与BrianFraseca所提出,DavidLuckham与BrianFraseca于1990年提出复合事件架构,使用模式比对、事件的相互关系......
  • 代理跨域
    在vue.config.js里写代码解决跨域问题1module.exports={2productionSourceMap:false,3//关闭eslint4lintOnSave:false,5//配置代理跨域......
  • React-hooks+TypeScript最佳实战
    ReactHooks什么是HooksReact一直都提倡使用函数组件,但是有时候需要使用state或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才......
  • React核心技术浅析
    1.JSX与虚拟DOM我们从React官方文档开头最基本的一段HelloWorld代码入手:ReactDOM.render(<h1>Hello,world!</h1>,document.getElementById('root'));这段代......
  • 宋红康老师关于动态代理举例的代码理解
    packageproxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;/**动态代理的举例*/interfaceH......
  • vue3引入onMounted后使用onMounted方法后控制台报错分析(Uncaught (in promise) TypeEr
    报错截图报错中已经大致的提示了,onmounted不是一个函数(方法),所以分析是vue没有找到,所以是引入的时候出了问题。Uncaught(inpromise)TypeError:(0,vue_reactivity__WEB......