浏览器和React中的事件机制

浏览器事件机制

DOM事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。事件传播分为三个阶段:

  • 捕获(Capture):事件对象从window对象传递到目标对象的过程。
  • 目标(target):目标节点在处理事件的过程。
  • 冒泡(Bubble):事件对象从目标对象传递到window对象的过程。

在任一阶段调用stopPropagation都将终止本次事件的传播。

网上kiang来的

注册事件

注册事件使用addEventListener(event, function, useCapture),第三个参数可以是布尔值,也可以是对象。是布尔值useCapture参数的情况下,默认值为false,表示注册事件是冒泡事件,为true时表示注册事件是捕获事件。

当是对象参数时,可以使用以下几个属性:

  • capture:布尔值,同useCapture
  • once:布尔值,值为true表示事件只会调用一次,调用以后移除监听
  • passive:布尔值,表示永远不会调用prevrntDedault

需要注意的是:event.target是指向引起触发事件的元素,event.currentTarget则是事件绑定的元素。

阻止冒泡和默认事件

为什么要阻止事件冒泡?这是因为某DOM节点绑定了某个事件监听器,当该DOM节点触发事件的时候才会执行回调函数,但是如果该节点的某后代节点触发了一个事件,也会由于事件冒泡导致该DOM节点的事件也被触发,在不应该的情况下执行了回调函数。

调用stopPropagation严格来说不是阻止冒泡,是阻止事件的传播,所以在捕获阶段也可以阻止。

调用stopImmediatePropagation同样能阻止事件,但是还能阻止该事件目标执行其他注册事件。

还有一种事件方式叫做preventDefault,它的作用不是用于阻止冒泡,而是阻止浏览器默认行为。如a标签跳转,表单提交等。

事件代理(事件委托)

如果一个节点中的子节点是动态生成的,那么子节点注册事件的时候应该注册在父节点上。这样避免了添加很多重复的事件监听器。

事件代理用到了两个JavaScript事件特性:事件冒泡以及目标元素。当一个元素上的事件被触发的时候,同样的事件将会在那个元素的所有祖先元素中被触发。

事件代理的处理方式有以下优点:

  • 节省内存
  • 不需要给子节点注销事件

React中的事件机制

React中的事件机制与原生的完全不同,时间没有绑定在原生DOM上,发出的事件也是对原生事件的包装。React内部事件系统可以分为两个阶段:事件注册和事件触发。

事件注册

React组件在组件加载(mount)和更新(update)时,其中的ReactDOMComponent会对传入的事件属性进行处理(_updateDOMProperties),对相关事件进行注册和存储。

React将所有的DOM事件全部注册到document节点上,事件绑定的主要方法是listenTo方法,事件全部调用ReactEventListenerdispatchEvent方法。

回调储存

事件绑定以后会执行putListener,该方法会在ReactReconcileTransaction事务的close阶段执行,具体由EventPluginHub来进行管理,根据事件的类型(type)和组件标识(_rootNodeID)作为key唯一标识事件并进行存储。

事件触发

事件执行时,document上绑定事件ReactEventListener.dispatchEvent会对事件进行分发,根据之前存储的类型和组件标识找到触发事件的组件。ReactEventEmitter利用EventPluginHub注入的plugins会将原生的DOM事件转化成合成的事件,然后批量执行存储的回调函数。

回调函数的执行分为两步:第一步是把所有的合成事件放到事件队列中,第二步是逐个执行。

常见问题

原生事件阻止冒泡会阻止合成事件的触发,而合成事件的阻止冒泡不影响原生组件。所以两者最好不要混合使用,否则会出现一些奇怪的问题。

React事件机制的优点:

  • 减少内存消耗,提升性能,一种事件类型只在document上注册一次
  • 统一规范,解决ie事件兼容问题,简化事件逻辑
  • 对开发者友好

猜你喜欢

转载自www.cnblogs.com/bzsheng/p/12519281.html