【React深入】React事件机制

关于React事件的疑问

1.为什么要手动绑定this

2.React事件和原生事件有什么区别

3.React事件和原生事件的执行顺序,可以混用吗

4.React事件如何解决跨浏览器兼容

5.什么是合成事件

下面是我阅读过源码后,将所有的执行流程总结出来的流程图,不会贴代码,如果你想阅读代码看看具体是如何实现的,可以根据流程图去源码里寻找。

事件注册

组件装载 / 更新。

通过lastProps、nextProps判断是否新增、删除事件分别调用事件注册、卸载方法。

调用EventPluginHub的enqueuePutListener进行事件存储

获取document对象。

根据事件名称(如onClick、onCaptureClick)判断是进行冒泡还是捕获。

判断是否存在addEventListener方法,否则使用attachEvent(兼容IE)。

给document注册原生事件回调为dispatchEvent(统一的事件分发机制)。

事件存储

EventPluginHub负责管理React合成事件的callback,它将callback存储在listenerBank中,另外还存储了负责合成事件的Plugin。

EventPluginHub的putListener方法是向存储容器中增加一个listener。

获取绑定事件的元素的唯一标识key。

将callback根据事件类型,元素的唯一标识key存储在listenerBank中。

listenerBank的结构是:listenerBank[registrationName][key]。

例如:

{

    onClick:{

        nodeid1:()=>{...}

        nodeid2:()=>{...}

    },

    onChange:{

        nodeid3:()=>{...}

        nodeid4:()=>{...}

    }

}

事件触发 / 执行

这里的事件执行利用了React的批处理机制,在前一篇的【React深入】setState执行机制中已经分析过,这里不再多加分析。

触发document注册原生事件的回调dispatchEvent

获取到触发这个事件最深一级的元素

例如下面的代码:首先会获取到this.child

      <div onClick={this.parentClick} ref={ref => this.parent = ref}>

        <div onClick={this.childClick} ref={ref => this.child = ref}>

          test

        </div>

      </div>

遍历这个元素的所有父元素,依次对每一级元素进行处理。

构造合成事件。

将每一级的合成事件存储在eventQueue事件队列中。

遍历eventQueue。

通过isPropagationStopped判断当前事件是否执行了阻止冒泡方法。

如果阻止了冒泡,停止遍历,否则通过executeDispatch执行合成事件。

释放处理完成的事件。

react在自己的合成事件中重写了stopPropagation方法,将isPropagationStopped设置为true,然后在遍历每一级事件的过程中根据此遍历判断是否继续执行。这就是react自己实现的冒泡机制。

合成事件

调用EventPluginHub的extractEvents方法。

循环所有类型的EventPlugin(用来处理不同事件的工具方法)。

在每个EventPlugin中根据不同的事件类型,返回不同的事件池。

在事件池中取出合成事件,如果事件池是空的,那么创建一个新的。

根据元素nodeid(唯一标识key)和事件类型从listenerBink中取出回调函数

返回带有合成事件参数的回调函数

总流程

将上面的四个流程串联起来。

为什么要手动绑定this

通过事件触发过程的分析,dispatchEvent调用了invokeGuardedCallback方法。

function invokeGuardedCallback(name, func, a) {

  try {

    func(a);

  } catch (x) {

    if (caughtError === null) {

      caughtError = x;

    }

  }

}

可见,回调函数是直接调用调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的this是undefined。

这里可以使用实验性的属性初始化语法 ,也就是直接在组件声明箭头函数。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。因此这样我们在React事件中获取到的就是组件本身了。

和原生事件有什么区别

React事件使用驼峰命名,而不是全部小写。

通过JSX, 你传递一个函数作为事件处理程序,而不是一个字符串。

例如,HTML:

<button onclick="activateLasers()">

  Activate Lasers

</button>

在React中略有不同:

<button onClick={activateLasers}>

  Activate Lasers

</button>

另一个区别是,在 React 中你不能通过返回false来阻止默认行为。必须明确调用preventDefault。

由上面执行机制我们可以得出:React自己实现了一套事件机制,自己模拟了事件冒泡和捕获的过程,采用了事件代理,批量更新等方法,并且抹平了各个浏览器的兼容性问题。

React事件和原生事件的执行顺序

  componentDidMount() {

    this.parent.addEventListener('click', (e) => {

      console.log('dom parent');

    })

    this.child.addEventListener('click', (e) => {

      console.log('dom child');

    })

    document.addEventListener('click', (e) => {

      console.log('document');

    })

  }

  childClick = (e) => {

    console.log('react child');

  }

  parentClick = (e) => {

    console.log('react parent');

  }

  render() {

    return (

      <div onClick={this.parentClick} ref={ref => this.parent = ref}>

        <div onClick={this.childClick} ref={ref => this.child = ref}>

          test

        </div>

      </div>)

  }

执行结果:

由上面的流程我们可以理解:

react的所有事件都挂载在document中

当真实dom触发后冒泡到document后才会对react事件进行处理

所以原生的事件会先执行

然后执行react合成事件

最后执行真正在document上挂载的事件

react事件和原生事件可以混用吗?

react事件和原生事件最好不要混用。

原生事件中如果执行了stopPropagation方法,则会导致其他react事件失效。因为所有元素的事件将无法冒泡到document上。

由上面的执行机制不难得出,所有的react事件都将无法被注册。

合成事件、浏览器兼容

  function handleClick(e) {

    e.preventDefault();

    console.log('The link was clicked.');

  }

这里,e是一个合成的事件。React根据 W3C 规范 定义了这个合成事件,所以你不需要担心跨浏览器的兼容性问题。

事件处理程序将传递SyntheticEvent的实例,这是一个跨浏览器原生事件包装器。 它具有与浏览器原生事件相同的接口,包括stopPropagation()和preventDefault(),在所有浏览器中他们工作方式都相同。

每个SyntheticEvent对象都具有以下属性:

boolean bubbles

boolean cancelable

DOMEventTarget currentTarget

boolean defaultPrevented

number eventPhase

boolean isTrusted

DOMEvent nativeEvent

void preventDefault()

boolean isDefaultPrevented()

void stopPropagation()

boolean isPropagationStopped()

DOMEventTarget target

number timeStamp

string type

React合成的SyntheticEvent采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。

另外,不管在什么浏览器环境下,浏览器会将该事件类型统一创建为合成事件,从而达到了浏览器兼容的目的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,477评论 1 11
  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,813评论 1 18
  • 前端开发面试题 面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。 题目类型: 理论知...
    怡宝丶阅读 2,572评论 0 7
  • 上一篇讲述了React事件的使用,这一篇主要讲述React的事件机制,主要包含事件注册、触发以及回调函数的存储,最...
    宁小姐的慢时光阅读 11,590评论 2 13
  • 版权声明:本文为博主原创文章,未经博主允许不得转载。 PS:转载请注明出处作者:TigerChain地址:http...
    TigerChain阅读 8,371评论 1 9