当前位置: 移动技术网 > IT编程>开发语言>JavaScript > React事件机制-事件注册

React事件机制-事件注册

2019年03月23日  | 移动技术网IT编程  | 我要评论

事件机制

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属性。根据当前workinprogresspendingprops对象,给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;  
    }
}

trapcapturedeventtrapbubbledevent的区别是前者注册捕获阶段的事件监听器,后者注册冒泡阶段的事件监听器。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把某一类型事件通过事件代理绑定到documentfragment上(fragment的情况比较少)。即workinprogresscomplete过程中,如果之前已经注册过onclick事件,后续workinprogress中的onclick事件将不再注册,统一由document中注册的click事件代理处理。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网