下面是我阅读过源码后,将所有的执行流程总结出来的流程图,不会贴代码,如果你想阅读代码看看具体是如何实现的,可以根据流程图去源码里寻找。
例如:
{ onClick:{ nodeid1:()=>{...} nodeid2:()=>{...} }, onChange:{ nodeid3:()=>{...} nodeid4:()=>{...} } }这里的事件执行利用了 React的批处理机制,在前一篇的【React深入】setState执行机制中已经分析过,这里不再多加分析。
触发 document注册原生事件的回调 dispatchEvent获取到触发这个事件最深一级的元素例如下面的代码:首先会获取到 this.child
<div onClick={this.parentClick} ref={ref => this.parent = ref}> <div onClick={this.childClick} ref={ref => this.child = ref}> test </div> </div> 遍历这个元素的所有父元素,依次对每一级元素进行处理。构造合成事件。将每一级的合成事件存储在 eventQueue事件队列中。遍历 eventQueue。通过 isPropagationStopped判断当前事件是否执行了阻止冒泡方法。如果阻止了冒泡,停止遍历,否则通过 executeDispatch执行合成事件。释放处理完成的事件。react在自己的合成事件中重写了 stopPropagation方法,将 isPropagationStopped设置为 true,然后在遍历每一级事件的过程中根据此遍历判断是否继续执行。这就是 react自己实现的冒泡机制。
将上面的四个流程串联起来。
通过事件触发过程的分析, dispatchEvent调用了 invokeGuardedCallback方法。
function invokeGuardedCallback(name, func, a) { try { func(a); } catch (x) { if (caughtError === null) { caughtError = x; } } }可见,回调函数是直接调用调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的 this是 undefined。
这里可以使用实验性的属性初始化语法 ,也就是直接在组件声明箭头函数。箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。因此这样我们在 React事件中获取到的就是组件本身了。
例如, HTML:
<button onclick="activateLasers()"> Activate Lasers </button>在 React 中略有不同:
<button onClick={activateLasers}> Activate Lasers </button>另一个区别是,在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault。
由上面执行机制我们可以得出: React自己实现了一套事件机制,自己模拟了事件冒泡和捕获的过程,采用了事件代理,批量更新等方法,并且抹平了各个浏览器的兼容性问题。
执行结果:
由上面的流程我们可以理解:
react的所有事件都挂载在 document中
当真实dom触发后冒泡到 document后才会对 react事件进行处理
所以原生的事件会先执行
然后执行 react合成事件
最后执行真正在 document上挂载的事件
react事件和原生事件最好不要混用。
原生事件中如果执行了 stopPropagation方法,则会导致其他 react事件失效。因为所有元素的事件将无法冒泡到 document上。
由上面的执行机制不难得出,所有的react事件都将无法被注册。
这里, 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 typeReact合成的 SyntheticEvent采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。
另外,不管在什么浏览器环境下,浏览器会将该事件类型统一创建为合成事件,从而达到了浏览器兼容的目的