前言
这是 react 事件机制的第一篇,主要内容有:表象理解,验证,意义和思考。
表象理解
先回顾下 对 react 事件机制基本理解,react 自身实现了一套自己的事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,虽然和原生的是两码事,但也是基于浏览器的事件机制下完成的。
我们都知道 react 的所有事件并没有绑定到具体的 dom 节点上而是绑定在了 document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。
上面是基于对 react 事件的一个基本的认知,那这个认知是否正确呢?我们可以通过简单的方法进行验证。
验证
验证内容:
- 所有事件均注册到了元素的最顶层 -document 上
- 节点的事件由统一的入口处理
为了方便,直接通过 cli 创建一个项目。
代码如下:
componentDidMount(){document.getElementById('btn-reactandnative').addEventListener('click', (e) => {console.log('原生 +react 事件: 原生事件执行');
});
}
handleNativeAndReact = (e) => {console.log('原生 +react 事件: 当前执行 react 事件');
}
handleClick=(e)=>{console.log('button click');
}
render(){
return <div className="pageIndex"><p>react event!!!</p
<button id="btn-confirm" onClick={this.handleClick}>react 事件 </button>
<button id="btn-reactandnative" onClick={this.handleNativeAndReact}> 原生 + react 事件 </button>
</div>
}
代码中给两个 button 绑定了合成事件,单独给 btn#btn-reactandnative 绑定了一个原生的事件。
然后看下 chrome 的控制台,查看元素上的注册事件。
经过简单的验证,可以看到所有的事件根据不同的事件类型都绑定在了 document 上。触发函数统一是 dispatchEvent。
试想一下
如果一个节点上同时绑定了合成和原生事件,那么禁止冒泡后执行关系是怎样的呢?
其实读到这里答案已经有了。我们现在基于目前的知识去分析下这个关系。
因为合成事件的触发是基于浏览器的事件机制来实现的,通过冒泡机制冒泡到最顶层元素,然后再由 dispatchEvent 统一去处理。
下面是我得出的结论:
原生事件阻止冒泡肯定会阻止合成事件的触发。
合成事件的阻止冒泡不会影响原生事件。
为什么呢?先回忆下浏览器事件机制
浏览器事件的执行需要经过三个阶段,捕获阶段 - 目标元素阶段 - 冒泡阶段。
节点上的原生事件的执行是在目标阶段,然而合成事件的执行是在冒泡阶段,所以原生事件会先合成事件执行,然后再往父节点冒泡。
既然原生都阻止冒泡了,那合成还执行个啥嘞。
好,轮到合成的被阻止冒泡了,那原生会执行吗?
当然会了。
因为原生的事件先于合成的执行,所以合成事件内阻止的只是合成的事件冒泡。(代码我就不贴了)
所以得出结论:
原生事件(阻止冒泡)会阻止合成事件的执行
合成事件(阻止冒泡)不会阻止原生事件的执行
两者最好不要混合使用,避免出现一些奇怪的问题
意义
react 自己做这么多的意义是什么?
我的理解的是
1. 减少内存消耗,提升性能,不需要注册那么多的事件了,一种事件类型只在 document 上注册一次
2. 统一规范,解决 ie 事件兼容问题,简化事件逻辑
3. 对开发者友好
思考
既然 react 帮我们做了这么多事儿,那他的背后的机制是什么样的呢?
事件怎么注册的,事件怎么触发的,冒泡机制怎样实现的呢?
请看后续文章 …..
更多精彩内容欢迎关注我的公众号 - 前端张大胖