React Event事件注册的实现
React 元素的事件处理和 DOM元素的很相似。但是有一点语法上的不同:
React事件绑定属性的命名采用驼峰式写法,而不是小写。
如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法)
并且 React 自己内部实现了一个合成事件,使用 React 的时候通常你不需要使用 addEventListener 为一个已创建的 DOM 元素添加监听器。你仅仅需要在这个元素初始渲染的时候提供一个监听器。
我们看一下这是怎么实现的
React 事件机制分为 事件注册,和事件分发,两个部分
事件注册
// 事件绑定 function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> );
上述代码中, onClick 作为一个 props 传入了一个 handleClick,在组件更新和挂载的时候,会对props处理, 流程如下:
核心代码:
在 ReactDOMComponent.js 进行组件加载 (mountComponent)、更新 (updateComponent) 的时候,调用 _updateDOMProperties
方法对 props 进行处理:
ReactDOMComponent.js
_updateDOMProperties: function(lastProps, nextProps, transaction) { ... if (registrationNameModules.hasOwnProperty(propKey)) { if (nextProp) { // 如果传入的是事件,去注册事件 enqueuePutListener(this, propKey, nextProp, transaction); } else if (lastProp) { deleteListener(this, propKey); } } ... } // 注册事件 function enqueuePutListener(inst, registrationName, listener, transaction) { var containerInfo = inst._nativeContainerInfo; var doc = containerInfo._ownerDocument; ... // 去doc上注册 listenTo(registrationName, doc); // 事务结束之后 putListener transaction.getReactMountReady().enqueue(putListener, { inst: inst, registrationName: registrationName, listener: listener, }); }
看下绑定方法
ReactBrowserEventEmitter.js
listento
//registrationName:需要绑定的事件 //当前component所属的document,即事件需要绑定的位置 listenTo: function (registrationName, contentDocumentHandle) { var mountAt = contentDocumentHandle; //获取当前document上已经绑定的事件 var isListening = getListeningForDocument(mountAt); ... if (...) { //冒泡处理 ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(...); } else if (...) { //捕捉处理 ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(...); } ... },
走到最后其实就是 doc.addEventLister(event, callback, false);
可以看出所有事件绑定在document上
所以事件触发的都是ReactEventListener的dispatchEvent方法
回调事件储存
listenerBank
react 维护了一个 listenerBank 的变量保存了所有的绑定事件的回调。
回到之前注册事件的方法
function enqueuePutListener(inst, registrationName, listener, transaction) { var containerInfo = inst._nativeContainerInfo; var doc = containerInfo._ownerDocument; if (!doc) { // Server rendering. return; } listenTo(registrationName, doc); transaction.getReactMountReady().enqueue(putListener, { inst: inst, registrationName: registrationName, listener: listener, }); }
当绑定完成以后会执行putListener。
var listenerBank = {}; var getDictionaryKey = function (inst) { //inst为组建的实例化对象 //_rootNodeID为组件的唯一标识 return '.' + inst._rootNodeID; } var EventPluginHub = { //inst为组建的实例化对象 //registrationName为事件名称 //listner为我们写的回调函数,也就是列子中的this.autoFocus putListener: function (inst, registrationName, listener) { ... var key = getDictionaryKey(inst); var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {}); bankForRegistrationName[key] = listener; ... } }
EventPluginHub在每个React中只实例化一次。也就是说,项目组所有事件的回调都会储存在唯一的listenerBank中。
相关推荐:
以上就是React Event事件注册的实现的详细内容,更多请关注其它相关文章!