React Event
事件绑定
1 | import React from "react"; |
this
需要指向 Demo 实例,获取 state。通过 call 或 bind 让 this 指向当前对象
通过 bind() 继承 Demo 实例属性,方法
1 | import React from "react"; |
this.clickEventPrototype.call(this)
立即调用,bind
this 预处理,运行后返回回调函数,点击后执行回调事件。
1 | import React from "react"; |
flowchart LR step1["解决 this 指向
this.clickEventPrototype()"] --> step2["解决 call 回调立即执行
this.clickEventPrototype.call(this)"] step2 --> step3["bind 解决了点击后调用回调
this.clickEventPrototype.bind(this)"]
箭头函数继承 this 实例属性,方法
1 | import React from "react"; |
合成事件[SyntheticBaseEvent]
它是围绕浏览器的原生事件,充当浏览器包装器对象;它们将不同浏览器的行为合并为一个 API,为了确保事件在不同浏览器中显示一致的属性。
合成事件原理
flowchart LR step1["Dom 元素添加合成事件属性"] --> step2["#root 上添加事件委托"]
DOM 元素添加合成事件属性
<button onClick={this.clickEventPrototype.bind(this)}>btn</button>
并不是给 button
添加了 addEventListener 事件,而是做了 给 #root 根容器(React17 以后的版本,React17 以前是给 docuemnt 容器)做的事件委托
在组件渲染阶段,jsx 会把 onXXX 或 onXXXCapture 赋给元素相关属性,并没有做事件绑定。
1 | // babel-compile.js |
#root上添加事件委托
react 合成事件源码实现
1 | var root = document.getElementById('root'), |
outer click 捕获合成
->inner click 捕获合成
->root click 捕获
->outer click 捕获
->inner click 捕获
->inner 冒泡
->outer 冒泡
->inner 冒泡合成
->outer 冒泡合成
->root 冒泡
事件触发后 react 执行原理
flowchart LR subgraph capture wc[window capture] --> dc[docuemnt capture] dc --> hc[html capture] hc --> bc[body capture] bc --> SyntheticBaseEvent([root 合成事件]):::orange SyntheticBaseEvent --> rc[root capture] rc --> oc[outer capture] oc --> ic[inner capture] end subgraph bubbling direction BT ib[inner bubbling] --> ob[outer bubbling] ob --> SyntheticBaseEventB([root 合成事件]):::orange SyntheticBaseEventB --> rb[root bubbling] rb --> bb[body bubbling] bb --> hb[html bubbling] hb --> db[docuemnt bubbling] db --> wb[window bubbling] end capture --> bubbling classDef orange fill:#f96
阻止事件传播
evt.stopPropagation
阻止 root 之后的事件传播,原生和合成事件传播都会阻止
evt.nativeEvent.stopPropagation
阻止 root 之后的原生事件传播
evt.nativeEvent.stopImmediatePropagation
立即阻止原生事件传播,当前 DOM 绑定的同类其它事件不是执行
react 16 事件传播机制
flowchart LR subgraph 捕获 wc[window capture] --> dc[docuemnt capture] dc --> hc[html capture] hc --> bc[body capture] bc --> rc[root capture] rc --> oc[outer capture] oc --> ic[inner capture] end subgraph 冒泡 direction BT ib[inner bubbling] --> ob[outer bubbling] ob --> rb[root bubbling] rb --> bb[body bubbling] bb --> hb[html bubbling] hb --> ocb([outer capture 合成]):::orange ocb --> stopPropagation{stopPropagation} ocb --> nativeEventStopPropagation{nativeEvent.stopPropagation} stopPropagation --> db[docuemnt bubbling] db --> e((end)) nativeEventStopPropagation --> icb([inner capture 合成]):::orange icb --> ibb([inner 合成]):::orange ibb --> obb([outer 合成]):::orange obb -->|nativeEvent.stopPropagation| db ocb --> stopImmediatePropagation{nativeEvent.stopImmediatePropagation} stopImmediatePropagation --> icb icb --> ibb ibb --> obb obb -->|nativeEvent.stopImmediatePropagation| e end 捕获 --> 冒泡 classDef orange fill:#f96
React17 以上,捕获阶段 root 事件执行处理事件委托中的捕获,冒泡阶段处理冒泡合成事件;React 17 以下版本,React 冒泡阶段处理 document 上委托的捕获、冒泡合成事件。
React16 事件缓存池
当生成的实例或者对象使用完毕之后,React就会将其所有属性置为null,然后丢进一个缓存池(Pool)中,后续再需要生成实例的时候,就不需要使用new再去创建一个对象了,直接从缓存池中拿一个置空后的实例/对象赋值之后使用就可以了,所以此处getPool就是判断缓存池是否有缓存,有的话就pop()出来一个赋值后使用,没有的话就new一个
React的源码中处处都有性能优化的影子。你可能会问,用完了不回收不会浪费内存么?确实会有这样的情况,但是这个缓存池的容量一般比较小,大多是10个左右,所以并不会占用太多空间
1 | <div className="inner" onClick={(evt) => { |