关于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
采纳了事宜池,如许做能够大大节约内存,而不会频仍的建立和烧毁事宜对象。
别的,不论在什么浏览器环境下,浏览器会将该事宜范例一致建立为合成事宜,从而达到了浏览器兼容的目标。