在了解完合成事件后,我不禁有一个疑问,如果我给一个dom
同时绑定合成事件与原生事件,到底谁会先执行呢?来看个例子:
class Echo extends Component {
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.querySelector(".button");
childrenDom.addEventListener('click', this.onDomClick, false);
}
onDomClick = (e) => {
console.log('原生事件click');
}
onReactClick = () => {
console.log('合成事件click');
}
render() {
return (
<div>
<button className="button" onClick={this.onReactClick}>点击</button>
</div>
)
}
}
为什么原生事件比合成事件快呢?通过上面的源码分析,其实很容易联想到,在冒泡到document
之前,原生事件已经被触发,这之后才到了document
开始事件派发,遍历数组进行react
合成事件callback
的执行,合成事件慢的合情合理。
哎?那如果我们同时给一个dom
绑定原生捕获事件与合成捕获事件呢?那按照这个说法,document
在最顶层,那是不是应该合成捕获事件要早于原生捕获事件执行呢?来看个例子:
class Echo extends Component {
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.querySelector(".button");
childrenDom.addEventListener('click', this.onDomClick, true);
}
onDomClick = (e) => {
console.log('原生事件捕获click');
}
onReactClick = () => {
console.log('合成事件捕获click');
}
render() {
return (
<div>
<button className="button" onClickCapture={this.onReactClick}>点击</button>
</div>
)
}
}
怎么还是原生事件早于合成事件的捕获阶段?????
在合成事件生成源码分析中,我们介绍了handleTopLevel
方法提到,合成事件是在当前节点冒泡不断向上搜集同名的合成事件回调,并且在traverseTwoPhase
这个方法中,通过正向负向两个遍历,去模拟的捕获与冒泡,说直白,根本不存在所谓的合成事件捕获,其实全都是靠冒泡搜集事件后,控制遍历顺序,来模拟了捕获与冒泡的事件执行顺序!!!
function traverseTwoPhase(inst, fn, arg) {
var path = [];
while (inst) {
path.push(inst);
inst = getParent(inst);
}
var i;
// 捕获倒序遍历
for (i = path.length; i-- > 0;) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
// 冒泡正向遍历
fn(path[i], 'bubbled', arg);
}
}
因此合成事件的捕获,说到底还是在原生事件冒泡之后,因为我不冒泡事件你都没搜集其,捕获个啥呢?
所以总结来说,合成事件不管捕获还是冒泡都晚于原生事件,结合之前的源码分析,非常合情合理!!来看下面这个例子加深印象:
class Echo extends Component {
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.querySelector(".button");
childrenDom.addEventListener('click', this.onDomChildClick, false);
childrenDom.addEventListener('click', this.onDomChildClickCapture, true);
parentDom.addEventListener('click', this.onDomParentClick, false);
parentDom.addEventListener('click', this.onDomParentClickCapture, true);
}
onDomChildClick = (e) => {
console.log('原生事件child--冒泡');
}
onDomChildClickCapture = (e) => {
console.log('原生事件child--捕获');
}
onDomParentClick = (e) => {
console.log('原生事件parent--冒泡');
}
onDomParentClickCapture = (e) => {
console.log('原生事件parent--捕获');
}
onReactChildClick = () => {
console.log('合成事件child--捕获');
}
onReactParentClick = () => {
console.log('合成事件parent--捕获');
}
render() {
return (
<div className="parent" onClickCapture={this.onReactParentClick}>
<button className="button" onClick={this.onReactChildClick}>点击</button>
</div>
)
}
}
总结合成事件与原生事件执行顺序:
相信到这里,你应该能不假思索的回答,如果在原生事件中阻止冒泡,那么事件执行都到不了document
,合成事件自然没机会去执行了,还是上面那个例子,我们修改如下代码:
onDomChildClick = (e) => {
e.stopPropagation()
console.log('原生事件child--冒泡');
}
在子元素原生冒泡阶段阻止冒泡,可以看到执行如下,整个合成事件都被阻止执行了。
原因其实在上面源码分析的executeDispatchesInOrder
方法中已经给出了答案:
if (Array.isArray(dispatchListeners)) {
for (var i = 0; i < dispatchListeners.length; i++) {
// 如果阻止冒泡,直接break跳出循环
if (event.isPropagationStopped()) {
break;
}
executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
}}
反过来呢?如果我们在合成事件冒泡阶段阻止冒泡,会影响原生事件吗?我想你心里已经有答案了:
class Echo extends Component {
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.querySelector(".button");
childrenDom.addEventListener('click', this.onDomChildClick, false);
parentDom.addEventListener('click', this.onDomParentClick, false);
}
onDomChildClick = (e) => {
console.log('原生事件child--冒泡');
}
onDomParentClick = (e) => {
console.log('原生事件parent--冒泡');
}
onReactChildClick = (e) => {
e.stopPropagation()
console.log('合成事件child--冒泡');
}
onReactParentClick = (e) => {
console.log('合成事件parent--冒泡');
}
render() {
return (
<div className="parent" onClick={this.onReactParentClick}>
<button className="button" onClick={this.onReactChildClick}>点击</button>
</div>
)
}
}
那么到这里,我们就解释了合成事件阻止冒泡对于原生事件的影响,当然在实际开发中,我们尽量还是别混用原生事件与合成事件。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!