Ссылочный код React, версия - ветка v15.6.1.
Реагировать на синтетические события
Почему существует абстракция синтетических событий?
Если к модели DOM привязано слишком много обработчиков событий, это может повлиять на весь отклик страницы и использование памяти. Чтобы избежать злоупотребления такими событиями DOM и скрыть различия в базовой системе событий между разными браузерами, React реализует средний уровень — SyntheticEvent.
принцип
В React, если нам нужно привязать события, мы часто пишем это в jsx:
<div onClick={this.onClick}>
react事件
</div>
Принцип примерно такой:
React не привязывает событие клика к реальному DOM div, но прослушивает все поддерживаемые события в документе.Когда событие происходит и всплывает в документе, React инкапсулирует содержимое события и передает его реальному обработчику для бежать .
Взяв приведенный выше код в качестве примера, весь жизненный цикл события показан следующим образом:
Среди них, поскольку объект события мультиплексирован, свойства будут очищены после выполнения функции обработки события, поэтому асинхронный доступ к свойствам события невозможен.event-pooling.
Как использовать нативные события в React
В то время как React инкапсулирует почти все нативные события, такие как:
- После включения модала нажмите другие пустые области, чтобы закрыть модаль
- Внедрение ряда сторонних библиотек для достижения оригинального события и необходимость взаимодействия друг с другом
При ожидании сценариев вы должны использовать собственные события для обработки бизнес-логики.
Поскольку нативные события должны быть привязаны к реальному DOM, они обычноcomponentDidMount阶段
/ref的函数执行阶段
Чтобы связать операцию, вcomponentWillUnmount阶段
Отмените привязку, чтобы избежать утечек памяти.
Пример выглядит следующим образом:
class Demo extends React.PureComponent {
componentDidMount() {
const $this = ReactDOM.findDOMNode(this)
$this.addEventListener('click', this.onDOMClick, false)
}
onDOMClick = evt => {
// ...
}
render() {
return (
<div>Demo</div>
)
}
}
Смешивание синтетических и нативных событий
Если вам нужно смешивать синтетические события и собственные события в бизнес-сценариях, вам нужно обратить внимание на следующие моменты во время использования:
Порядок ответа
Давайте посмотрим на простой пример, нажмите на следующий примерDemoКак будет выглядеть консольный вывод в будущем?
class Demo extends React.PureComponent {
componentDidMount() {
const $this = ReactDOM.findDOMNode(this)
$this.addEventListener('click', this.onDOMClick, false)
}
onDOMClick = evt => {
console.log('dom event')
}
onClick = evt => {
console.log('react event')
}
render() {
return (
<div onClick={this.onClick}>Demo</div>
)
}
}
Давайте проанализируем это: сначала выполняется прослушиватель событий DOM, затем событие продолжает всплывать к документу, и снова выполняется прослушиватель синтетических событий.
То есть окончательный вывод консоли:
dom event react event
перестань пузыриться
Тогда, если вonDOMClick
вызыватьevt.stopPropagation()
Шерстяная ткань?
Поскольку события DOM защищены от всплытия и не могут достичь документа, синтетическое событие не будет запущено, а вывод консоли будет выглядеть так:
dom event
Простые примеры легче понять, а примеры сложнее:
class Demo extends React.PureComponent {
componentDidMount() {
const $parent = ReactDOM.findDOMNode(this)
const $child = $parent.querySelector('.child')
$parent.addEventListener('click', this.onParentDOMClick, false)
$child.addEventListener('click', this.onChildDOMClick, false)
}
onParentDOMClick = evt => {
console.log('parent dom event')
}
onChildDOMClick = evt => {
console.log('child dom event')
}
onParentClick = evt => {
console.log('parent react event')
}
onChildClick = evt => {
console.log('child react event')
}
render() {
return (
<div onClick={this.onParentClick}>
<div className="child" onClick={this.onChildClick}>
Demo
</div>
</div>
)
}
}
если вonChildClick
вызыватьevt.stopPropagtion()
, вывод консоли становится следующим:
child dom event parent dom event child react event
Этот результат связан с тем, что функция stopPropagation, которая инкапсулирует синтетическое событие React, добавляет к себе флаг isPropagationStopped при вызове, чтобы определить, выполняются ли последующие слушатели.
Исходный код выглядит следующим образом:
// https://github.com/facebook/react/blob/v15.6.1/src/renderers/shared/stack/event/EventPluginUtils.js
for (var i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
}
// Listeners and Instances are two parallel arrays that are always in sync.
if (dispatchListeners[i](event, dispatchInstances[i])) {
return dispatchInstances[i];
}
}
Неловкое положение nativeEvent в системе событий React
У некоторых людей могут возникнуть сомнения, хотя синтетическое событие находится позже, чем нативное событие в последовательности ответа, может ли синтетическое событие повлиять на выполнение слушателя нативного события? Ответ (почти) невозможен. . .
Мы знаем, что входные параметры, полученные в прослушивателях событий React, не являются нативными событиями браузера, а нативные события можно передавать черезevt.nativeEvent
чтобы получить. Но, к сожалению, nativeEvent делает очень мало.
stopPropagation
В ожиданиях пользователей,stopPropagation
используется для предотвращения всплытия собственного события текущего DOM.
Однако, в соответствии с принципом синтеза событий из предыдущего раздела, фактически при вызове этого метода фактический эффект заключается в предотвращении всплытия в самом внешнем слое DOM, что не соответствует ожиданиям.
stopImmediatePropagation
stopImmediatePropagation
Часто используется для предотвращения ненужного выполнения в нескольких прослушивателях событий при смешивании нескольких сторонних библиотек.
Но в системе React компонент может быть привязан только к одному обработчику событий одного и того же типа (при повторном определении более поздний обработчик перезапишет предыдущий), поэтому синтетические события даже не инкапсулируются.stopImmediatePropagation
.
На самом деле nativeEventstopImmediatePropagation
Только прослушиватели событий, привязанные к документу, могут быть заблокированы. Кроме того, посколькуПроблема с порядком привязки событий, следует отметить, что еслиreact-dom.jsСобытие документа связано перед загрузкой,stopImmediatePropagation
Это также не остановить.
[Непопулярно] Синтетические события на этапе захвата
Документации по синтетическим событиям много, и нужно найти что-то новое. . .
React поддерживает регистрацию прослушивателей на этапе захвата, но поскольку сценариев приложений не так много, об этом упоминают редко. Однако в этой статье используются синтетические события, которые будут расширены вместе.
Слегка изменим этот пример, как теперь будет выглядеть вывод консоли?
class Demo extends React.PureComponent {
componentDidMount() {
const $parent = ReactDOM.findDOMNode(this)
const $child = $parent.querySelector('.child')
$parent.addEventListener('click', this.onParentDOMClick, true)
$child.addEventListener('click', this.onChildDOMClick, false)
}
onParentDOMClick = evt => {
console.log('captrue: parent dom event')
}
onChildDOMClick = evt => {
console.log('bubble: child dom event')
}
onParentClick = evt => {
console.log('capture: parent react event')
}
onChildClick = evt => {
console.log('bubble: child react event')
}
render() {
return (
<div onClickCapture={this.onParentClick}>
<div className="child" onClick={this.onChildClick}>
Demo
</div>
</div>
)
}
}
оказаться:
captrue: parent dom event bubble: child dom event capture: parent react event bubble: child react event
Это кажется разумным, но это не кажется разумным. Некоторые люди (такие как я) могут быть сбиты с толку, почему фазовая реакция захвата синтетических событий также позже, чем фазовая реакция пузырьков нативных событий?
На самом деле это происходит потому, что прокси синтетического события не регистрирует обработчики событий в фазе захвата/бублинга в документе одновременно, а используются только обработчики событий в фазе всплытия.Каждый раз, когда DOM событие запускается, React будетevent._dispatchListeners
Вставьте все функции, которые необходимо выполнить, а затем выполните их в цикле (как в исходном коде React выше).
а также_dispatchListeners
Логика генерации следующая:
// https://github.com/facebook/react/blob/v15.6.1/src/renderers/dom/client/ReactDOMTreeTraversal.js
/*
path为react的组件树,由下向上遍历,本例中就是[child, parent];
然后先将标记为captured的监听器置入_dispatchListeners,此时顺序是path从后往前;
再是标记为bubbled的监听器,顺序是从前往后。
*/
function traverseTwoPhase(inst, fn, arg) {
var path = [];
while (inst) {
path.push(inst);
inst = inst._hostParent;
}
var i;
for (i = path.length; i-- > 0; ) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
fn(path[i], 'bubbled', arg);
}
}
В заключение
- Прослушиватели синтетических событий единообразно регистрируются в документе и имеют только фазу всплытия. Таким образом, встроенные прослушиватели событий всегда отвечают раньше, чем синтетические прослушиватели событий.
- После предотвращения всплытия нативного события это предотвратит выполнение прослушивателя синтетического события.
- Синтетическое событие наносительно в этом сценарии, нет шерсти