Извините, что ссылка на странице Nuggets недействительна, в будущем это будет исправлено.
TL;DR
В этой статье анализируется система событий и исходный код React, чтобы ответить на два вопроса: «Почему React должен реализовывать систему событий сам по себе?» и «Как работает система событий React?» Для производительности и повторного использования React принимает идеи прокси-сервера событий, пула, пакетного обновления, кросс-браузерной и кросс-платформенной совместимости, монтирует прослушиватели событий в документы, конструирует синтетические события и внутренне имитирует набор захвата и мошенничества. и запуск функции обратного вызова реализует собственный набор систем событий.
- Если у вас есть всего несколько минут, я предлагаю вам посмотреть его прямо сейчас.анимационная часть.
- Если у вас есть полчаса, вы можете прочитать его по порядку, не обращая внимания на исходный код.
- Если вас интересует система событий React, рекомендуется клонировать исходный код React (исходный код, указанный в этой статье, взят изv16.5.0), а затем читайте по порядку.
Начинать
Недавно при использовании React для рефакторинга интерфейса проекта я и мои коллеги столкнулись с некоторыми странными проблемами. Поэтому я потратил некоторое время на изучение исходного кода React. Темой этой статьи является система событий React. Я стараюсь исключить сложные технические детали. Я надеюсь ответить на два вопроса простым и интуитивно понятным способом, а именно: "Почему React должен быть реализовал сам" Система событий?"а также«Как работает система событий React?»**.
Иногда вещи могут стать удивительно беспорядочными, если вы не знаете, как они работают…
два простых примера
Пример 1
-
Согласно приведенному ниже коду, что будет выведено после нажатия кнопки? (отсортировано по ABCD)
-
если я положу
innerClick
серединаe.stopPropagation()
Плюс, что будет на выходе? (отсортировано по ABCD)
class App extends React.Component {
innerClick = e => {
console.log("A: react inner click.");
// e.stopPropagation();
};
outerClick = () => {
console.log("B: react outer click.");
};
componentDidMount() {
document
.getElementById("outer")
.addEventListener("click", () => console.log("C: native outer click"));
window.addEventListener("click", () =>
console.log("D: native window click")
);
}
render() {
return (
<div id="outer" onClick={this.outerClick}>
<button id="inner" onClick={this.innerClick}>
BUTTON
</button>
</div>
);
}
}
Правильный ответ - это (чтобы предотвратить заглядывание, промах левого
1.
C: native outer click
A: react inner click.
B: react outer click.
D: native window click
2.
C: native outer click
A: react inner click.
Примеры дикарбоновой кислоты
Ожидается, что форма будет отредактирована после нажатия кнопки редактирования, и в это время кнопка «Изменить» становится кнопкой отправки, а кнопка отправки нажимается для отправки формы. код показывает, как показано ниже
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
editable: false
};
}
handleClick = () => {
console.log("edit button click!!");
this.setState({ editable: true });
};
handleSubmit = e => {
console.log("submit event!!");
e.preventDefault(); //避免页面刷新
};
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.state.editable ? (
<button type="submit">submit</button>
) : (
<button type="button" onClick={this.handleClick}>edit</button>
)}
</form>
);
}
}
Но на самом деле мы обнаружили, что при нажатии кнопки редактирования срабатывает событие отправки формы. почему мы нажали одинtype="button"
Кнопка сработаетsubmit
А события?
Имея в виду эти два примера, мы подошли к теме этой статьи.Я просто хочу сразу перейти к ответу?
Почему React сам реализует систему событий?
Я думаю, что этот вопрос в основном дляпредставлениеа такжемультиплексРассмотрим два аспекта.
Прежде всего, с точки зрения производительности, React как фреймворк на уровне View получает vDOM путем рендеринга, а затем алгоритм diff определяет, какие узлы в DOM-дереве нужно добавить, заменить или изменить. непосредственно вставляется в узел DOM, а затем вызывает частые вызовыaddEventListener
а такжеremoveEventListener
, что приводит к потере производительности. Итак, React принимаетагент событийМетод для большинства событий прослушивает документ, а затем оценивает узел, запущенный событием, в соответствии с целью в событии. (За исключением нескольких событий, которые не всплывают в документе, например видео и т. д.)
Во-вторых, React синтетическийSyntheticEvent
УсыновленныйбассейнИдея состоит в том, чтобы сэкономить память и избежать частого создания и уничтожения объектов событий. Это также, если нам нужно асинхронно использоватьsyntheticEvent
, нужно выполнитьevent.persist()
Причина предотвращения освобождения объекта события.
Наконец, пакет можно увидеть повсюду в исходном коде React.Массовое обновление, в основном все, что можно пакетировать (наиболее распространенныеsetState
) React сохранит промежуточный процесс и оставит его в конце перед сбросом. Так же, как браузер выполняет Style, Layout и Paint в дереве DOM, он не будет работать.ele.style.color='red';
Сразу после этого он только упаковывает эти операции и, наконец, выполняет рендеринг, когда это необходимо.
ele.style.color='red';
ele.style.color='blue';
ele.style.color='red';
浏览器只会渲染一次
Для повторного использования React видит, что события в пользовательском интерфейсе на самом деле очень похожи в разных браузерах и платформах, таких как обычныеclick
,change
и т.п. React надеется инкапсулировать нативные события разных платформ вSyntheticEvent
.
- сделатьРазные платформы могут использовать одну и ту же систему событий, добавляя EventEmitter и соответствующий Renderer., присоединяйтесь на веб-платформе
ReactBrowserEventEmitter
, добавлено в NativeReactNativeEventEmitter
. Как показано на рисунке ниже, для разных платформ React нужно заменить только левую часть, а правую частьEventPluginHub
Детали можно использовать повторно. - а такжеДля разных браузеров React помогает нам унифицировать события и обеспечить совместимость браузеров., например для
transitionEnd
,webkitTransitionEnd
,MozTransitionEnd
а такжеoTransitionEnd
, React будет сгруппирован вtopAnimationEnd
, поэтому нам нужно обработать только это одно стандартное событие.
Проще говоря, точно так же, как jQuery помогает нам решить проблему совместимости между различными браузерами, React делает шаг вперед и помогает нам унифицировать совместимость различных платформ, так что нам нужно учитывать только стандартизированные события при разработке.
Как работает система событий React?
привязка события
Давайте посмотрим, что мы написали в JSX.onClick
Как Handler записывается на узле Dom иdocument
монитор.
React связывает большинство событий, используяtrapBubbledEvent
а такжеtrapCapturedEvent
Эти две функции зарегистрированы. Как показано выше, когда мы выполняемrender
илиsetState
После этого система планирования React's Fiber будет выполнена перед окончательной фиксацией в дереве DOM.trapBubbledEven
илиtrapCapturedEvent
,
выполнивaddEventListener
Привяжите соответствующий узел документаdispatch
В качестве обработчика, ответственного за мониторинг типаtopLevelType
мероприятие.
здесьdispatchInteractiveEvent
а такжеdispatchEvent
Разница между двумя callback-функциями в том, что React16 начал заменять оригинальную Stack Reconciliation на Fiber для достижения асинхронного рендеринга (он по-прежнему не включен по умолчанию, и его все равно нужно использоватьunstable_
API в начале, эта функция связана с примером 2, будет вПоследняя картинка в статье объясняет), поэтому в случае асинхронного рендеринга я дважды нажимаю кнопку, затем, когда вторая кнопка отвечает, она может быть вызвана в обработчике первой кнопкиsetState
Он еще не был окончательно зафиксирован в дереве DOM.В настоящее время результат первой кнопки необходимо сбросить и зафиксировать в дереве DOM для обеспечения согласованности. будет использоваться в это времяdispatchInteractiveEvent
. можно понимать какdispatchInteractiveEvent
Перед выполнением он гарантирует, что все предыдущие операции были зафиксированы в дереве DOM, а затем запускает свой собственный процесс и, наконец, запускаетdispatchEvent
. Однако, поскольку React по-прежнему выполняет синхронный рендеринг, текущая производительность этих двух функций согласуется, и я надеюсь, что React17 принесет нам функцию асинхронного рендеринга, которая включена по умолчанию.
Теперь, когда мы прослушали события на узле документа, теперь нам нужно посмотреть, как сохранить обработчик, который мы написали в jsx, на соответствующем узле.
React в конечном итоге будет вызывать каждый раз, когда мы создаем или обновляем узелcreateInstance
илиcommitUpdate
эти две функции, обе из которых в конечном итоге вызовутupdateFiberProps
Эта функция будетprops
это нашonClick
,onChange
Подождите, пока обработчик будет сохранен на узле DOM.
До сих пор мы прослушивали событие в документе и сохраняли обработчик в соответствующем узле DOM. Далее нам нужно увидеть, как React прослушивает и обрабатывает нативные события браузера и, наконец, запускает соответствующий обработчик.
триггер события
Здесь я сделаланимация, я надеюсь, что это может помочь вам понять. Нажмите зеленую кнопку > «Играть дальше».
Извините за необходимость вставлять ссылку, наггетсы не позволяют вставлять фреймы.
с простымclick
События в качестве примера, через привязку событий мы имеемdocument
мониторclick
События, когда мы фактически нажимаем кнопку, как нативное событие попадает в юрисдикцию React? как синтезироватьSyntheticEvent
А как имитировать захват и барботирование? и наконец то, что мы написали в jsxonClick
Как наконец запускается обработчик? Имея в виду эти вопросы, давайте рассмотрим фазу, запускаемую событием.
Я буду примерно использовать следующий рисунок для разбора кода, слева я нажимаю привязкуhandleClick
Стек вызовов js после кнопки, код каждого шага справа, а некоторые коды, не влияющие на понимание, удалены. Я надеюсь, что таким образом всем будет проще понять механизм запуска событий React.
Когда мы нажимаем кнопку,click
Событие в конечном итоге перейдет к документу и вызовет обработчик, который мы прослушиваем в документе.dispatchEvent
, затем срабатываетbatchedUpdates
.batchedUpdates
Код в этом формате будет часто появляться в исходном коде React.пакетная обработкаВещи будут сначала собраны, а затем разобраны за один раз.
Вы можете увидеть значение по умолчаниюisBatching
ложно, когда вызывается один разbatchedUpdates
,isBatching
Значение станет истинным в это время, если в следующем вызове есть непрерывный вызовbatchedUpdates
, он будет выполнен непосредственноhandleTopLevel
, В настоящее времяsetState
и т. д. не будут обновляться в DOM. пока стек вызовов не вернется к первому вызовуbatchedUpdates
Только тогда все результаты будут сброшены (обновлены в DOM) вместе.
Некоторые учащиеся могут спросить стек вызововBatchedUpdates$1
что это такое? Или если рендерер браузера и нативный рендерер подключены к системе событий React?
По сути, система событий React предоставляет функциюsetBatchingImplementation
, используемый для динамического монтирования рендереров разных платформ, что также отражает систему событий React复用
.
здесьinteractiveUpdates
а такжеbatchedUpdates
Разница была объяснена выше и не будет повторяться здесь.
handleTopLevel
позвонюrunExtractedEventsInBatch()
, которая является самой важной функцией для обработки событий React. Как мы видим на анимации выше, вEventEmitter
То, что делается внутри, на самом деле в основном состоит из двух шагов этой функции.
- Первым шагом является синтез синтетических событий на основе собственных событий, имитация всплывающего захвата в vDOM и сбор всех обратных вызовов событий, которые необходимо выполнить для формирования массива обратных вызовов.
- Второй шаг — пройти массив обратного вызова и вызвать функцию обратного вызова.
первый звонокextractEvents
, пройти в нативном событииe
, система событий React синтезирует синтетические события на основе возможных подключаемых модулей событий.Synthetic e
. Здесь мы видим, что вызовEventConstructor.getPooled()
, возьмите синтетический объект события из пула событий, если пул событий пуст, создайте новый синтетический объект события, который отражает, что React реализовал синтетический объект события для повышения производительности.бассейнподумал о.
Затем передайте Propagator, смоделируйте захват и всплытие в vDOM и соберите все обратные вызовы событий и соответствующие узлы, которые необходимо выполнить.traverseTwoPhase
Имитируются две стадии захвата и барботирования.Реализация здесь очень хитрая.Короче говоря, он обходит массив вперед и назад. Затем для каждого узла вызовитеlistenerAtPhase
Извлеките функцию обратного вызова, установленную на узле, когда событие связано, и добавьте ее в массив обратного вызова.
Затем повторите все синтетические события. Здесь вы можете видеть, что при обработке события React вызоветevent.isPersistent()
Чтобы увидеть, нужно ли сохранять синтетическое событие, если нет, синтетическое событие будет выпущено, поэтому, когда нам нужно асинхронно прочитать синтетическое событие, нам нужно выполнитьevent.persist()
, в противном случае React выпускает событие здесь.
Наконец, вот время, когда функция обратного вызова фактически запускается, выньте массив обратного вызоваevent._dispatchListeners
, пройти через функцию обратного вызова триггера. и черезevent.isPropagationStopped()
Этот шаг должен имитировать остановку пузырения. Здесь мы видим, что React не волнует, вызываем мы его или нет, когда он собирает массив обратного вызова.stopPropagation
, но проверит, нужно ли останавливать пузырение на стадии срабатывания.
В этот момент срабатывает функция обратного вызова события, если она выполняетсяsetState
Подождите, пока стек вызовов не вернётся на дноinteractiveUpdate
При окончательном сбросе vDOM создается, согласовывается и, наконец, фиксируется в DOM.
Это весь процесс срабатывания события, вы можете вернуться и посмотреть на него еще разанимация, я думаю, вы лучше поймете этот процесс.
ПримерОтладка
Теперь, когда мы знакомы с системой событий React, вернемся к двум метафизическим вопросам в начале статьи, давайте посмотрим, почему?
Пример 1
Если вы хотите просмотреть содержание темы или забыть о ней, вы можете нажатьздесьПроверить.
Я считаю, что после прочтения этой статьи, если вы уже имеете какое-то представление о системе событий React, этот вопрос не должен вызвать затруднений.
- Поскольку прослушиватель событий React подключен к документу, собственная система
#outer
обратный вызов на слушателеC
Он будет выведен первым; затем собственное событие всплывает в документе в системе событий React, а система событий React имитирует захват всплывающего вывода.A
а такжеB
; Наконец, система событий React выполняется и возвращается в браузер, чтобы продолжить всплывать в окне, выводитьD
. - родная система в
#outer
обратный вызов на слушателеC
Сначала будет выполнено; затем родное событие всплывает в документе для входа в систему событий React, а выводA
, в обработке событий React#inner
называетсяstopPropagation
, событие перестает всплывать.
Итак, лучше всегоНе смешивайте систему событий React и собственную систему событий., если смешано, убедитесь, что вы точно знаете, чего ожидать.
Пример 2
Если вы хотите просмотреть содержание темы или забыть о ней, вы можете нажатьздесьПроверить.
Этот вопрос немного сложнее. Сначала мы нажимаемedit
Браузер кнопок запускаетclick
Событие всплывает в документе и входит в систему событий React, а React выполняет обратный вызов.setState
, система событий React завершила обработку события. Поскольку React в настоящее время отображается синхронно, React выполняетperformSyncWork
Измените кнопку наtype="submit"
, поскольку все узлы в одном месте и теги являются кнопками, React повторно использует этот узел кнопки (По конкретным причинам см.) и обновите его в DOM. В этот момент браузерclick
Выполнение обработки событий продолжается, обнаруживаяtype="submit"
, а под формой соответствующий триггерsubmit
мероприятие.
Решений много, добавляйте кнопку к кнопкеkey
;Две кнопки пишутся отдельно, не используйте троичные и т.п. для решения проблемы.
Для получения дополнительной информации вы можете взглянуть на график вызовов ниже, который должен быть хорошо понят.Если есть что-то, что вы не можете понять, пожалуйста, оставьте сообщение ниже, и я постараюсь объяснить это как можно яснее.
Еще один момент: «setState является асинхронным».
Я считаю, что для многих React-разработчиков следует часто слышать фразу «setState is asynchronous», я помню, что часто видел это предложение, когда впервые изучал React, а потом говорил, что если нужно использовать предыдущее состояние, нужно использовать в setStatesetState((preState)=>{})
Сюда.
Но это не совсем точно. Правильное утверждение должно бытьsetState иногда асинхронен, setState синхронен относительно браузера
На данный моментsetState
Он асинхронен в жизненном цикле и обратном вызове события, то есть будет собираться и обрабатываться пакетами. В других случаях, таких как обещание, setTimeout выполняется синхронно, то есть вызов setState один раз отобразит один раз и обновит его до DOM.Если вы не верите, вы можете нажатьздесьпытаться.
И когда стек вызовов JS пуст, результат должен быть обновлен до DOM (синхронный рендеринг). Это означает, что setState должен быть синхронным по отношению к браузеру. Как показано ниже
Блок-схема асинхронного рендеринга примерно такая, как показана на рисунке ниже Когда я недавно думал об этой проблеме, я обнаружил, что если сейчас асинхронный рендеринг, то наш пример 2 станет случайной ямой, потому что еслиsetState
Результат DOM не был обновлен до DOM, браузер не инициирует событие отправки.
Тем не менее, команда React разрабатывала концепцию асинхронного рендеринга в течение двух лет, а React16 внедрила согласование Fiber и предоставила API для асинхронного рендеринга.unstable_xxx
, я считаю, что в React17 мы можем наслаждаться улучшением производительности, вызванным асинхронным рендерингом, благодаря команде React.
Суммировать
Я надеюсь, что после прочтения этой статьи вы сможете получить простое представление о системе событий React. Знайте «Почему React необходимо реализовать собственную систему событий?» и «Как работает система событий React?». Реагировать напредставлениеа такжемультиплекс, принимает идеи прокси-сервера событий, пула, пакетного обновления, кросс-браузерной и кросс-платформенной совместимости, монтирует прослушиватели событий в документе, создает синтетические события и внутренне моделирует набор функций захвата, всплытия и триггера обратного вызова. собственный набор систем событий.
Если вы все еще неясны, нашли ошибки или упущения в статье или просто обменялись соответствующими вопросами, пожалуйста, оставьте сообщение ниже, и я сделаю все возможное, чтобы ответить и ответить на ваши вопросы. Если вам нравятся мои статьи, подписывайтесь на меня имой блог,Спасибо.