React事件机制-事件注册
程序员文章站
2022-05-25 18:28:03
事件机制 React事件主要分为两部分: 事件注册与事件分发。下面先从事件注册说起。 事件注册 假设我们的程序如下: 事件注册主要发生在初始化Dom属性的时候,调用 方法,对一些类型dom进行事件绑定。 js switch (tag) { case 'iframe': case 'object': ......
事件机制
react事件主要分为两部分: 事件注册与事件分发。下面先从事件注册说起。
事件注册
假设我们的程序如下:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>react app</title> </head> <body> <div id="root"></div> </body> </html>
import react from 'react'; import reactdom from 'react-dom'; class clickcounter extends react.component { constructor(props) { super(props); this.state = { count: 0 }; } handleclick = () => { this.setstate((state) => { return {count: state.count + 1}; }); }; render() { return [ <button key="1" onclick={this.handleclick}>update counter</button>, <span key="2">{this.state.count}</span>, ] } } reactdom.hydrate(<clickcounter />, document.getelementbyid('root'));
事件注册主要发生在初始化dom属性的时候,调用setinitialproperties
方法,对一些类型dom进行事件绑定。
switch (tag) { case 'iframe': case 'object': trapbubbledevent(top_load, domelement); props = rawprops; break; case 'video': case 'audio': for (var i = 0; i < mediaeventtypes.length; i++) { trapbubbledevent(mediaeventtypes[i], domelement); } props = rawprops; break; ... } setinitialdomproperties(tag, domelement, rootcontainerelement, props, iscustomcomponenttag); ...
接着调用setinitialdomproperties
来真正初始化dom属性。根据当前workinprogress
的pendingprops
对象,给dom对象设置属性。其中,有个分支会专门处理事件。
// registrationnamemodules是一个map对象,存储着react支持的事件类型 else if (registrationnamemodules.hasownproperty(propkey)) { if (nextprop != null) { ensurelisteningto(rootcontainerelement, propkey); } }
执行ensurelisteningto
方法:
// rootcontainerelement为react应用的挂载点, registrationname为onclick function ensurelisteningto(rootcontainerelement, registrationname) { // 判断rootcontainerelement是document还是fragment var isdocumentorfragment = rootcontainerelement.nodetype === document_node || rootcontainerelement.nodetype === document_fragment_node; // 获取rootcontainerelement所在的document。 var doc = isdocumentorfragment ? rootcontainerelement : rootcontainerelement.ownerdocument; listento(registrationname, doc); }
开始执行listento
方法,注册事件入口。
// 获取当前已监听的原生事件类型的map var islistening = getlisteningfordocument(mountat); // 获取对应的原生事件类型,registrationnamedependencies存储了react事件类型与浏览器原生事件类型映射的一个map var dependencies = registrationnamedependencies[registrationname]; for (var i = 0; i < dependencies.length; i++) { var dependency = dependencies[i]; if (!(islistening.hasownproperty(dependency) && islistening[dependency])) { switch (dependency) { ...// 除了scroll blur focus cancel close方法调trapcapturedevent方法,invalid submit reset不处理之外,其余都调trapbubbledevent方法。 default: var ismediaevent = mediaeventtypes.indexof(dependency) !== -1; if (!ismediaevent) { trapbubbledevent(dependency, mountat); } break; } // 标记该原生事件类型已被注册,下次注册同类型事件时会被忽略 islistening[dependency] = true; } }
trapcapturedevent
与trapbubbledevent
的区别是前者注册捕获阶段的事件监听器,后者注册冒泡阶段的事件监听器。trapcapturedevent
使用比较少,所以重点看下trapbubbledevent
。
//click document function trapbubbledevent(topleveltype, element) { if (!element) { return null; } // 从字面意能看出,前者是交互类事件,优先级会比普通事件高(click的分发者是dispatchinteractiveevent) var dispatch = isinteractivetopleveleventtype(topleveltype) ? dispatchinteractiveevent : dispatchevent; // 注册事件,在冒泡阶段捕获 addeventbubblelistener(element, getraweventname(topleveltype), // check if interactive and wrap in interactiveupdates dispatch.bind(null, topleveltype)); }
总结
可以发现,react把某一类型事件通过事件代理绑定到document
或fragment
上(fragment
的情况比较少)。即workinprogress
在complete
过程中,如果之前已经注册过onclick
事件,后续workinprogress
中的onclick
事件将不再注册,统一由document
中注册的click
事件代理处理。
上一篇: javascript面向对象习题答案
下一篇: Mac 系统隐藏文件功能指南