Автор этой статьи:речная вода
предисловие
React предоставляет нам виртуальную систему событий. Как работает эта виртуальная система событий? Автор разобрал исходный код и организовал следующие документы для вашего ознакомления.
существуетВведение в React-событияВ этой статье представлены синтетические объекты событий и причины их предоставления. Основная причина заключается в том, что React хочет реализовать полнобраузерную структуру. Для достижения этой цели необходимо предоставить совместимую с полноценным браузером систему событий для плавного из разных браузеров различия устройств.
Синтетический объект события очень интересен, сначала вы почувствуете себя странно, когда услышите название, но еще более странно, когда увидите английское название.SyntheticEvent
, на самом деле синтез событий означает синтез события React с использованием собственных событий, таких как использование собственных событий.click
событие синтезированоonClick
события, используя роднойmouseout
событие синтезированоonMouseLeave
События, местное событие и большинство из них являются одним из синтетических типов события, и только когда дело доходит до проблем совместимости, нам необходимо использовать, событие не соответствует синтезу. Синтетический инцидент React не был первым, проблемы, возникающие, возникшие в 300 мс на iOS, представили FastClick с использованием сенсорного события, синтезированные событию Click, также считали приложением синтетического события.
Поняв, что события React являются синтетическими событиями, мы посмотрим на события с другой точки зрения, например, на код, который мы часто пишем в коде.
<button onClick={handleClick}>
Activate Lasers
</button>
мы уже знаем этоonClick
Это просто синтетическое событие, а не нативное, так что же произошло за это время? Как соотносятся нативные события и синтетические события?
Вышеприведенный код выглядит очень просто, на самом деле механизм работы системы событий React намного сложнее, чем приведенный выше. Его принцип работы условно делится на два этапа.
- привязка события
- триггер события
Давайте посмотрим, как работают эти два этапа.Здесь мы в основном анализируем уровень исходного кода и берем содержимое исходного кода 16.13 в качестве эталона.
1. Как React связывает события?
Поскольку React предоставляет синтетические события, вам нужно знать, как синтетические события соотносятся с нативными событиями.Это соответствие хранится в плагине событий React.EventPlugin
, Подключаемые модули событий можно рассматривать как React, инкапсулирующие различные функции обработки синтетических событий в модуль, каждый модуль обрабатывает только свои собственные соответствующие синтетические события, так что различные типы событий могут быть отделены в коде, например, таргетингonChange
События имеют отдельныйLegacyChangeEventPlugin
плагин для обработки, дляonMouseEnter
,onMouseLeave
использоватьLegacyEnterLeaveEventPlugin
плагин для обработки.
Чтобы узнать соответствие между синтетическими событиями и нативными событиями, React загружает все плагины событий в начале.Эта часть логики находится вReactDOMClientInjectionкод показывает, как показано ниже
injectEventPluginsByName({
SimpleEventPlugin: LegacySimpleEventPlugin,
EnterLeaveEventPlugin: LegacyEnterLeaveEventPlugin,
ChangeEventPlugin: LegacyChangeEventPlugin,
SelectEventPlugin: LegacySelectEventPlugin,
BeforeInputEventPlugin: LegacyBeforeInputEventPlugin
});
После регистрации вышеупомянутого плагина,EventPluginRegistry(В старой версии кода этот модуль называетсяEventPluginHub
) В этом модуле инициализируются некоторые глобальные объекты.Есть несколько объектов более важных и их можно обсудить отдельно.
Первый объектregistrationNameModule, который содержит сопоставление событий React с соответствующим плагином. Это примерно так. Он содержит все типы событий, поддерживаемые React. Самая большая функция этого объекта — определить, является ли реквизит компонента типом события, который используется при обработке собственных реквизитов Компонента, если реквизит в этом объекте будет рассматриваться как событие.
{
onBlur: SimpleEventPlugin,
onClick: SimpleEventPlugin,
onClickCapture: SimpleEventPlugin,
onChange: ChangeEventPlugin,
onChangeCapture: ChangeEventPlugin,
onMouseEnter: EnterLeaveEventPlugin,
onMouseLeave: EnterLeaveEventPlugin,
...
}
Второй объектregistrationNameDependencies, этот объект выглядит следующим образом
{
onBlur: ['blur'],
onClick: ['click'],
onClickCapture: ['click'],
onChange: ['blur', 'change', 'click', 'focus', 'input', 'keydown', 'keyup', 'selectionchange'],
onMouseEnter: ['mouseout', 'mouseover'],
onMouseLeave: ['mouseout', 'mouseover'],
...
}
Этот объект является отображением синтетических событий на нативные события, о которых мы упоминали в начале.onClick
а такжеonClickCapture
События, полагайтесь только на собственныеclick
мероприятие. Но дляonMouseLeave
Он опирается на дваmouseout
,mouseover
, что указывает на то, что это событие используется Reactmouseout
а такжеmouseover
Аналоговый синтез. Именно из-за этого поведения, даже если он делает реактивный, способный синтезировать многие браузеры, не поддерживают событие для нашего кода для использования.
Третий объектplugins, этот объект представляет собой список всех плагинов, зарегистрированных выше.
plugins = [LegacySimpleEventPlugin, LegacyEnterLeaveEventPlugin, ...];
Прочитав вышеизложенную информацию, давайте развернёмся и посмотрим на очередной рядовойEventPlugin
На что это похоже. Плагин — это объект, который содержит следующие два свойства.
// event plugin
{
eventTypes, // 一个数组,包含了所有合成事件相关的信息,包括其对应的原生事件关系
extractEvents: // 一个函数,当原生事件触发时执行这个函数
}
Знание приведенной выше информации будет очень полезно для нас, чтобы проанализировать принцип работы событий React.Давайте начнем фазу привязки событий.
- React выполняет операцию сравнения, чтобы отметить, какие из нихтип документаУзлы должны быть добавлены или обновлены.
- При обнаружении необходимости создать узел или обновить узел используйтеregistrationNameModuleПроверьте, является ли реквизит типом события, и если да, перейдите к следующему шагу.
- пройти черезregistrationNameDependenciesПроверьте, от чего зависит это событие Reactродной тип события.
- Проверьте, зарегистрированы ли один или несколько из этих собственных типов событий, и проигнорируйте их.
- Если собственный тип события не был зарегистрирован, зарегистрируйте собственное событие в
document
выше, обратный вызов предоставляется ReactdispatchEventфункция.
Вышеприведенное описание этапа:
- Мы регистрируем все типы событий на
document
начальство. - Все встроенные прослушиватели событий
dispatchEvent
функция. - Один и тот же тип события React будет связывать нативное событие только один раз, например, независимо от того, сколько мы пишем
onClick
, конечная реакция будет иметь только одну на событие DOMlistener
. - React не использует нашу бизнес-логику
listener
Привязан к оригинальному событию, ни поддерживать подобноеeventlistenermap
вещи для хранения нашихlistener
.
Из 3-4 правил можно сделать вывод, что наша бизнес-логикаlistener
Это вообще не имеет никакого отношения к фактическому событию DOM. React только гарантирует, что это собственное событие может быть захвачено само по себе, а затем React отправит обратный вызов нашего события. Когда наша страница переключается, React ничего не может сделать, поэтому свободен от операцияremoveEventListener
или синхронизироватьeventlistenermap
Следовательно, эффективность его выполнения будет значительно повышена, что эквивалентно глобальному делегированию событий.Даже если отрисовывается большой список, разработчику не нужно заботиться о проблеме привязки событий.
2. Как React запускает события?
Мы знаем, что, поскольку все типы событий связаны с ReactdispatchEvent
Функция, так что вы можете управлять некоторыми общими поведениями в глобальном масштабе.Ниже приведен весь процесс поведения.
export function dispatchEventForLegacyPluginEventSystem(
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
): void {
const bookKeeping = getTopLevelCallbackBookKeeping(
topLevelType,
nativeEvent,
targetInst,
eventSystemFlags
);
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
batchedEventUpdates(handleTopLevel, bookKeeping);
} finally {
releaseTopLevelCallbackBookKeeping(bookKeeping);
}
}
bookKeeping
Он сохраняется для иерархической взаимосвязи компонентов при выполнении события, то есть, если структура компонента изменится в процессе выполнения события, это не повлияет на процесс срабатывания события.
Весь процесс триггерного события выглядит следующим образом:
- Любое событие срабатывает, выполняется
dispatchEvent
функция. -
dispatchEvent
воплощать в жизньbatchedEventUpdates(handleTopLevel)
,batchedEventUpdatesвключит переключатель пакетного рендеринга и вызоветhandleTopLevel
. - handleTopLevelбудет выполняться последовательноpluginsВсе плагины событий в.
- Если плагин определяет тип события, которое ему нужно обработать, он обрабатывает это событие.
Для большинства событий логика обработки следующая, т.е.LegacySimpleEventPlugin
Плагины делают свою работу
- Какой синтетический тип события использовать, определяется собственным типом события (объектом-оболочкой собственного события, например
SyntheticMouseEvent
). - Если в пуле объектов есть экземпляр этого типа, извлеките экземпляр, перезапишите его свойства и используйте его в качестве объекта события, отправленного на этот раз (повторное использование объекта события), если нет, создайте новый экземпляр.
- Найдите соответствующий узел DOM из нативного события click, найдите ближайший экземпляр компонента React из узла DOM и найдите цепочку, образованную родительским узлом этого экземпляра вверх.Эта цепочка — это цепочка, которую мы хотим вызвать синтетическое событие. , ( содержит только компоненты нативного типа,
div
,a
такие родные компоненты).
- Запустите эту цепочку в обратном порядке, родитель -> ребенок, смоделируйте фазу захвата, запустите все реквизиты, содержащие
onClickCapture
пример.
- Запустите эту цепочку вперед, ребенок -> родитель, смоделируйте фазу всплытия, запустите все реквизиты, содержащие
onClick
пример.
Эти этапы иллюстрируют следующие явления:
- Синтетические события React можно использовать только в цикле событий, потому что этот объект, скорее всего, будет повторно использоваться другими этапами, если вы хотите сохранить его, вам нужно вызвать его вручную.
event.persist()
Скажите React, что этот объект должен быть сохранен. (Устарело в React17) - React пузырится и ловит на самом деле не пузырится и не ловится на уровне DOM
- React активирует все соответствующие узлы в нативном событии.
onClick
события, которые выполняют этиonClick
Раньше React включал переключатель пакетного рендеринга, этот переключательsetState
в асинхронную функцию. - События вступают в силу только для собственных компонентов, пользовательские компоненты не срабатывают.
onClick
.
3. Что мы узнали из системы событий React
- React16 связывает нативные события с
document
начальство.
Это легко понять, события React на самом деле находятся вdocument
срабатывает.
- Полученный нами объект события является синтетическим событием React, и объект события нельзя использовать вне события.
Итак, следующее неправильное использование
function onClick(event) {
setTimeout(() => {
console.log(event.target.value);
}, 100);
}
- React включит пакетные обновления при отправке события, после чего все
setState
стать асинхронным.
function onClick(event) {
setState({a: 1}); // 1
setState({a: 2}); // 2
setTimeout(() => {
setState({a: 3}); // 3
setState({a: 4}); // 4
}, 0);
}
В настоящее время 1 и 2 являются асинхронными в событии, оба они запускают операцию рендеринга только один раз, 3 и 4 синхронны, а 3 и 4 каждый запускает рендеринг.
- React
onClick
/onClickCapture
, на самом деле все они происходят в фазе всплытия исходного события.
document.addEventListener('click', console.log.bind(null, 'native'));
function onClickCapture() {
console.log('capture');
}
<div onClickCapture={onClickCapture}/>
Здесь мы используемonClickCapture
, но на самом деле он все еще всплывает для нативных событий, поэтому React 16 на самом деле не поддерживает события захвата привязки.
- Поскольку все события регистрируются в событии верхнего уровня,
ReactDOM.render
Будут конфликты.
Если мы визуализируем поддерево, созданное с использованием другой версии экземпляра React, то даже если поддерево вызываетсяe.stopPropagatio
События продолжают распространяться. Таким образом, несколько версий React конфликтуют с событиями.
Наконец, мы можем легко понять архитектурную схему системы событий React.
4. Что нового в системе событий в React 17
Сейчас выпущен React 17, официально названный обновлением без новых функций.Для пользователей он не предоставляет взрывных функций, таких как Hooks, и не имеет серьезных рефакторингов, таких как Fiber, но накопил множество исправлений ошибок и исправил предыдущие. Есть много недостатков. Самым большим изменением является трансформация системы событий.
Ниже приведены некоторые обновления функций, связанные с событиями, которые перечисляет автор.
Настроен для связывания события верхнего уровня к контейнеру, Reactom.runder (приложение, контейнер);
Привязать событие верхнего уровня кcontainer
на вместоdocument
Это может решить проблему сосуществования нескольких версий, с которой мы столкнулись, что является основным преимуществом решения с микроинтерфейсом.
Выравнивание нативных событий браузера
React 17 наконец-то поддерживает встроенную поддержку событий захвата в соответствии с собственными стандартами браузера.
в то же времяonScroll
События больше не всплывают.
onFocus
а такжеonBlur
использовать роднойfocusin
,focusout
синтез.
Aligning with Browsers
Мы внесли несколько небольших изменений, связанных с системой событий: Событие onScroll больше не всплывает, чтобы предотвратить общую путаницу. События React onFocus и onBlur переключились на использование встроенных событий focusin и focusout, которые более точно соответствуют существующему поведению React и иногда предоставляют дополнительную информацию. События фазы захвата (например, onClickCapture) теперь используют настоящие прослушиватели фазы захвата браузера.
Отменить мультиплексирование событий
Официальное объяснение заключается в том, что производительность повторного использования объектов событий не была значительно улучшена в современных браузерах, но людям легко использовать ее неправильно, поэтому от этой оптимизации просто отказываются.
Ссылаться на
Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы набираем front-end, iOS и Android круглый год.Если вы готовы сменить работу и любите облачную музыку, присоединяйтесь к нам на grp.music-fe(at)corp.netease.com!