React笔记-事件注册

react

事件机制

本系列以React v16.8.3为基础进行源码分析

React事件主要分为两部分: 事件注册与事件分发。下面先从事件注册说起。

事件注册

假设我们的程序如下:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>React App</title>

</head>

<body>

<div ></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事件代理处理。

更好的阅读体验在我的github,欢迎????提issue。

以上是 React笔记-事件注册 的全部内容, 来源链接: utcz.com/z/381858.html

回到顶部