媒介
这是 react 事宜机制的第三节 – 事宜注册,经由历程本文你将相识react 事宜的注册历程,以及在这个历程当中重要经由了哪些症结步骤,同时连系源码举行考证和加强邃晓。
文章触及到的源码是基于 react15.6.1版本,虽然不是最新版本然则也不会影响我们对 react 事宜机制的团体把握和邃晓。
文中不会说异常细节的内容,而是会把也许的流程和原理性的内容举行引见,做到对团体流程有个认知和邃晓。
内容纲要
- 重要做两件事 (事宜注册、事宜存储)
- 大抵流程
- 细致实行历程
- 总结
1. 重要做两件事
依据我的邃晓,react 事宜注册历程实在重要做了2件事:
a. 事宜注册
b. 事宜存储
a. 事宜注册 – 组件挂载阶段,依据组件内的声明的事宜范例-onclick,onchange 等,给 document 上增加事宜 -addEventListener,并指定一致的事宜处置惩罚顺序 dispatchEvent。
b. 事宜存储 – 就是把 react 组件内的一切事宜一致的存放到一个处所,也就是缓存起来,可以邃晓成放入一个对象内,为了在触发事宜的时刻可以查找到对应的要领去实行。
再配个图
2. 大抵流程
上面大抵说了事宜注册须要完成的两个目的,那完成目的的历程须要经由哪些症结处置惩罚呢?
起首 react 拿到将要挂载的组件的假造 dom(实在就是 react dom, 相似一个对象),然后处置惩罚react dom 的 props ,推断属性内是不是有声明为事宜的属性,比方onclick,这个时刻获得事宜范例 click 和对应的事宜处置惩罚顺序 fn,然后直行背面3步
a. 实行事宜注册
b. 将react dom ,事宜范例,处置惩罚函数 fn 放入数组存储
c. 组件挂载完成后,处置惩罚 b 步骤天生的数组,经由遍历把事宜处置惩罚函数存储到listenerBank中
再配个图
3.细致实行历程
3.1 得先从 jsx 提及
看个最熟习的代码,也是我们一样寻常的写法
handleFatherClick=()=>{
}
handleChildClick=()=>{
}
render(){
return <div className="box">
<div className="father" onClick={this.handleFatherClick}>
<div className="child" onClick={this.handleChildClick}>child </div>
</div>
</div>
}
经由 babel 编译后,可以看到终究挪用的要领是react.createElement,而且声明的事宜范例和回调也是一个props。
react.createElement实行的效果会返回一个所谓的假造 dom(react element 或许 react dom),看下图
3. 2 最先处置惩罚props,拿到事宜范例和回调 fn
ReactDOMComponent在举行组件加载(mountComponent)、更新(updateComponent)的时刻,须要对props举行处置惩罚(_updateDOMProperties):
可以看下 registrationNameModules 的内容,就不细说了。
3.3 注册事宜和事宜的存储
【注册事宜】
接着上面的代码实行到了这个要领
enqueuePutListener(this, propKey, nextProp, transaction);
在这个要领里会举行事宜的注册以及事宜的存储,包含冒泡和捕捉的处置惩罚
依据当前的组件实例猎取猎取到最高父级-也就是document,然后实行要领 listenTo – 也是最症结的一个要领,举行事宜绑定处置惩罚
源码文件:ReactBrowerEventEmitter.js
末了实行EventListener.listen(冒泡)或许EventListener.capture(捕捉),
单看下冒泡的注册,实在就是addEventListener的第三个参数是 false
也可以看到注册事宜的时刻也对 ie 做了兼容。
上面没有看到 dispatchEvent 的定义,下面可以看到传入 dispatchEvent 要领的代码。
到这里事宜注册就完事儿了。
【事宜存储】
下一步最先事宜的存储,在 react 里一切事宜的触发都是经由历程 dispatchEvent要领一致举行派发的,而不是在注册的时刻直接注册声明的回调,来看下怎样存储的 。
【事宜存储结论】
react 把一切的事宜和事宜范例以及react 组件举行关联,把这个关联保存在了一个 map里,也就是一个对象里(键值对),然后在事宜触发的时刻去依据当前的组件id和事宜范例查找到对应的事宜。
再加个浅易图
看源码:
function enqueuePutListener(inst, registrationName, listener, transaction) {
var containerInfo = inst._hostContainerInfo;
var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
listenTo(registrationName, doc);//这个要领上面已说完
//这里触及到了事件,事物会在今后的章节再引见,重要看事宜注册
//下面的代码是将putListener放入数组,当组件挂载完后会顺次实行数组的回调。也就是putListener会顺次实行
transaction.getReactMountReady().enqueue(putListener, {
inst: inst,//组件实例
registrationName: registrationName,//事宜范例 click
listener: listener //事宜回调 fn
});
}
function putListener() {
var listenerToPut = this;
//放入数组,回调行列
EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);
}
大抵的流程就是实行完listenTo(事宜注册),然后实行 putListener 要领举行事宜存储,一切的事宜都邑存储到一个对象中 – listenerBank,细致由EventPluginHub举行治理。
//拿到组件唯一标识 id
var getDictionaryKey = function getDictionaryKey(inst) {
return '.' + inst._rootNodeID;
}
putListener: function putListener(inst, registrationName, listener) {
//获得组件 id
var key = getDictionaryKey(inst);
//获得listenerBank对象中指定事宜范例的对象
var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
//存储回调 fn
bankForRegistrationName[key] = listener;
//....
}
listenerBank实在就是一个二级 map,如许的构造更轻易事宜的查找。
这里的组件 id 就是组件的唯一标识,然后和fn 举行关联,在触发阶段就可以找到相干的事宜回调。
看下listenerBank构造:
看到这个构造是不是是很熟习呢?就是我们寻常运用的 object.
到这里大抵的流程已说完,是不是是觉得有点邃晓又不大邃晓。
没紧要,再来个细致的图,从新邃晓下
4.末了
本文重如果从团体流程上引见了下 react 事宜中事宜的注册历程,并没有深切到源码的细节,有兴致的小伙儿可以自查下源码,也愿望本文可以带给你一些启示,若文章有表述不清或有题目的处所迎接留言交换。
更多精彩内容迎接关注我的民众号-前端张大胖