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

在这里插入图片描述

壹 ❀ 引

在前面两篇文章中,我们花了较大的篇幅介绍reactsetState方法,在介绍setState同步异步时提到,在react合成事件中react对于this.state更新都是异步,但在原生事件中更新却是同步,这说明react在合成事件处理上必定与原生事件存在部分差异,那么本篇文章就来着重介绍react中的合成事件,在文章开始之前我们先罗列部分问题:

  • 了解原生事件监听机制吗?为什么需要事件代理?
  • react如何实现的合成事件?这么做的好处是什么?
  • 合成事件与原生事件执行先后顺序。
  • 合成事件阻止冒泡会阻塞原生事件吗?原生事件阻止冒泡会阻塞合成事件吗?

在文章开始前,大家可以先自行思考这些问题,假设这些在面试中遇到,你能回答多少呢?那么本文开始。

贰 ❀ 从原生事件说起

虽然本文的核心重点是介绍react的合成事件,但文章开头既然给了合成事件与原生事件相关对比问题,因此了解原生事件是有必要的,考虑到可能有同学对此类知识存在遗忘,这里做个简单复习。

首先让我们复习下事件监听api addEventListener,基本语法如下:

element.addEventListener(event, function, useCapture)

其中element表示你需要监听的dom元素;event表示监听的事件类型,比如onclick,onblurfunction表示触发事件后的回调,你需要做什么都可以写在这里;useCapture是一个布尔值,表示是否开启捕获阶段,默认false

以如下代码结构为例,当我们点击span时,必然会经历过捕获阶段----》目标阶段----》冒泡阶段

在这里插入图片描述

我们用一个例子复习这个过程:

<div id="div">
    我是div
    <p id="p">
        我是p
        <span id="span">我是span</span>
    </p>
</div>
const div = document.querySelector("#div");
const p = document.querySelector("#p");
const span = document.querySelector("#span");
// 捕获阶段,这里将useCapture设置为true
div.addEventListener("click",()=>console.log("捕获阶段--div"),true);
p.addEventListener("click",()=>console.log("捕获阶段--p"),true);
// 目标阶段
span.addEventListener("click",()=>console.log("目标阶段--span"));
// 冒泡阶段,useCapture默认false,不写了
div.addEventListener("click",()=>console.log("冒泡阶段--div"));
p.addEventListener("click",()=>console.log("冒泡阶段--p"));

在这里插入图片描述

既然提到了事件监听,那么有三个API就不得不提了,它们分别是event.preventDefaultevent.stopPropagationevent.stopImmediatePropagation。让我们先聊聊stopPropagation,此方法一般用于阻止冒泡,比如父子都绑定了点击事件,但点击子时我不希望父在冒泡阶段也被触发,因此通过在子的事件回调中添加此方法能做到这一点,修改上述例子中目标阶段的代码为:

span.addEventListener("click", (e) => {
   
   
    e.stopPropagation();
    console.log("目标阶段--span")
});

此时点击span会发现只会输出span,而冒泡阶段的divp都被阻止执行了。

在这里插入图片描述

关于event.preventDefault,此方法常用于阻止元素默认行为,比如点击a标签除了执行我们绑定的click事件外,它还会执行a标签默认的跳转。再或者form表达点击提交会将form的值传递给action指定地址并刷新页面,像这类行为我们均可以通过preventDefault阻止。

在介绍stopImmediatePropagation之前,我们需要知道事件监听相对于普通事件绑定的一大好处是,事件监听支持为同一dom监听多个行为,但如果是普通的事件绑定后者会覆盖前者:

span.onclick = ()=>console.log('事件绑定-1');
// 后绑定的事件会覆盖前面的绑定
span.onclick = ()=>console.log('事件绑定-2');
// 事件监听就不会存在覆盖,下面2个都会执行
span.addEventListener("click", (e) => {
   
   
    console.log("事件监听-1")
});
span.addEventListener("click", (e) => {
   
   
    console.log("事件监听-2")
});

在这里插入图片描述

那既然事件监听支持为同一dom绑定多个,我在执行了某个监听后,需要将其它监听都阻止掉怎么办?此时就轮到stopImmediatePropagation出场立大功了,看个例子:

// 捕获阶段
div.addEventListener("click", () => console.log("捕获阶段--div-1"), true);
div.addEventListener("click", () => console.log("捕获阶段--div-2"), true);
p.addEventListener("click", () => console.log("捕获阶段--p-1"), true);
p.addEventListener("click", () => console.log("捕获阶段--p-2"), true);
// 目标阶段
span.addEventListener("click", (e) => {
   
   
    e.stopImmediatePropagation();
    console.log("目标阶段---span-1")
});
span.addEventListener("click", (e) => {
   
   
    console.log("目标阶段---span-2")
});
// 冒泡阶段
div.addEventListener("click", () => console.log("冒泡阶段--div-1"));
div.addEventListener("click", () => console.log("冒泡阶段--div-2"));
p.addEventListener("click", () => console.log("冒泡阶段--p-1"));
p.addEventListener("click", () => console.log("冒泡阶段--p-2"));

在这里插入图片描述

可以看到stopImmediatePropagation同样会阻止事件冒泡,但除此之外,它还会阻止同一dom身上的其它事件执行。

那么聊完事件监听,什么是事件代理?在现实生活中,我们网购到公司的快递大部分都会由前台代签收,而不是分别送到我们每个人手上,此时前台就相当于做了一个代理的事情,原本需要不同的多个人分别签收的行为,统一与前台代理处理。

映射到代码中,假设有ul>li的结构,我们希望点击li显示出li的文本内容,如果给每个li绑定就得这么写:

<ul id="ul">
    <li onclick="handleClick(event)">1</li>
    <li onclick="handleClick(event)">2</li>
    <li onclick="handleClick(event)">3</li>
    <li onclick="handleClick(event)">4</li>
    <li onclick="handleClick(event)">5</li>
</ul>
const handleClick=(e)=>{
   
   
    console.log(e.target.innerHTML);
};

但如果通过事件代码,我们将点击行为委托给li共同的父元素ul,代码将更为清晰简单:

<ul id="ul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
const span = document.getElementById
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值