react 深入了解react合成事件底层原理,原生事件中阻止冒泡是否会阻塞合成事件?三


风晓
风晓 2024-01-01 07:08:06 51513 赞同 0 反对 0
分类: 资源
接上一篇react 深入了解react合成事件底层原理,原生事件中阻止冒泡是否会阻塞合成事件?二

肆 ❀ 合成事件与原生事件执行先后

在了解完合成事件后,我不禁有一个疑问,如果我给一个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>
    )
  }
}

那么到这里,我们就解释了合成事件阻止冒泡对于原生事件的影响,当然在实际开发中,我们尽量还是别混用原生事件与合成事件。

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
风晓L1
粉丝 1 资源 2038 + 关注 私信
最近热门资源
银河麒麟桌面操作系统备份用户数据  123
统信桌面专业版【全盘安装UOS系统】介绍  116
银河麒麟桌面操作系统安装佳能打印机驱动方法  108
银河麒麟桌面操作系统 V10-SP1用户密码修改  101
最近下载排行榜
银河麒麟桌面操作系统备份用户数据 0
统信桌面专业版【全盘安装UOS系统】介绍 0
银河麒麟桌面操作系统安装佳能打印机驱动方法 0
银河麒麟桌面操作系统 V10-SP1用户密码修改 0
作者收入月榜
1

prtyaa 收益393.62元

2

zlj141319 收益217.85元

3

1843880570 收益214.2元

4

IT-feng 收益208.98元

5

风晓 收益208.24元

6

777 收益172.71元

7

Fhawking 收益106.6元

8

信创来了 收益105.84元

9

克里斯蒂亚诺诺 收益91.08元

10

技术-小陈 收益79.5元

请使用微信扫码

加入交流群

请使用微信扫一扫!