Это второй день моего участия в августовском испытании обновлений, подробности о мероприятии:Испытание августовского обновления
Недавно у друга было интервью, и интервьюер задал странный вопрос, который я написал в заголовке.
Чтобы иметь возможность задать этот вопрос, интервьюер не должен очень хорошо знать React. Также возможно, что в резюме интервьюируемого написано, что он знаком с React. Интервьюер хочет использовать этот вопрос, чтобы судить, действительно ли интервьюируемый знаком с React. Реагируйте 🤣.
Правилен ли вопрос интервьюера?
Вопрос интервьюера:setState
Является ли это макрозадачей или микрозадачей, то в его познанииsetState
Определенно асинхронная операция. судитьsetState
Независимо от того, является ли это асинхронной операцией или нет, вы можете сначала провести эксперимент, создать новый проект React через CRA и отредактировать в проекте следующий код:
import React from 'react';
import logo from './logo.svg';
import './App.css';
class App extends React.Component {
state = {
count: 1000
}
render() {
return (
<div className="App">
<img
src={logo} alt="logo"
className="App-logo"
onClick={this.handleClick}
/>
<p>我的关注人数:{this.state.count}</p>
</div>
);
}
}
export default App;
Страница выглядит так:
Приведенный выше логотип React привязан к событию клика, и теперь нам нужно реализовать это событие клика После нажатия на логотип нам нужно сделать это один раз.setState
операции, распечатайте журнал, когда заданная операция завершена, а перед заданной операцией добавьте макрозадачу и микрозадачу соответственно. код показывает, как показано ниже:
handleClick = () => {
const fans = Math.floor(Math.random() * 10)
setTimeout(() => {
console.log('宏任务触发')
})
Promise.resolve().then(() => {
console.log('微任务触发')
})
this.setState({
count: this.state.count + fans
}, () => {
console.log('新增粉丝数:', fans)
})
}
Очевидно, что после нажатия на Логотип первый завершенныйsetState
операции, а затем запуск микрозадач и запуск макрозадач. так,setState
Время выполнения раньше, чем микрозадача и макрозадача, даже в этом случае можно только сказать, что время его выполнения раньше, чемPromise.then
, еще не доказывает, что это синхронная задача.
handleClick = () => {
const fans = Math.floor(Math.random() * 10)
console.log('开始运行')
this.setState({
count: this.state.count + fans
}, () => {
console.log('新增粉丝数:', fans)
})
console.log('结束运行')
}
Глядя на это таким образом, кажетсяsetState
Еще одна асинхронная операция. Основная причина в том, что в жизненном цикле React и связанном потоке событий всеsetState
Операция сначала будет кэширована в очереди, а ранее кэшированная будет извлечена после завершения всего события или завершения процесса монтирования.setState
Очередь выполняет расчет, запуская обновление состояния. Пока мы выпрыгиваем из потока событий или жизненного цикла React, мы можем разорвать пары React.setState
контроль. Самый простой способ - поставитьsetState
ставитьsetTimeout
в анонимной функции.
handleClick = () => {
setTimeout(() => {
const fans = Math.floor(Math.random() * 10)
console.log('开始运行')
this.setState({
count: this.state.count + fans
}, () => {
console.log('新增粉丝数:', fans)
})
console.log('结束运行')
})
}
Отсюда видно, чтоsetState
По сути, он все еще находится в цикле событий, и он не переключается на другую макрозадачу или микрозадачу, он реализован на основе синхронного кода в работе, но по поведению выглядит асинхронным. Так что к интервьюеру вообще нет вопросов.
Как React контролирует setState?
В предыдущем случаеsetState
только вsetTimeout
станет похоже на синхронный метод, как это делается?
handleClick = () => {
// 正常的操作
this.setState({
count: this.state.count + 1
})
}
handleClick = () => {
// 脱离 React 控制的操作
setTimeout(() => {
this.setState({
count: this.state.count + fans
})
})
}
Давайте сначала рассмотрим предыдущий код.В этих двух операциях мы записываем стек вызовов в Performance и видим, в чем разница между двумя стеками вызовов.
В стеке вызовов вы можете увидетьComponent.setState
метод в конечном итоге будет вызванenqueueSetState
метод, при этомenqueueSetState
метод будет вызываться внутриscheduleUpdateOnFiber
метод, разница в том, что при обычном вызовеscheduleUpdateOnFiber
метод будет вызывать толькоensureRootIsScheduled
, будет вызываться после завершения метода событияflushSyncCallbackQueue
метод. Покидая поток событий React,scheduleUpdateOnFiber
существуетensureRootIsScheduled
После завершения вызова он будет вызван напрямуюflushSyncCallbackQueue
метод, этот метод используется для обновления состояния и повторного рендеринга.
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
if (lane === SyncLane) {
// 同步操作
ensureRootIsScheduled(root, eventTime);
// 判断当前是否还在 React 事件流中
// 如果不在,直接调用 flushSyncCallbackQueue 更新
if (executionContext === NoContext) {
flushSyncCallbackQueue();
}
} else {
// 异步操作
}
}
Вышеприведенный код может кратко описать этот процесс, в основном, чтобы судитьexecutionContext
Равен ли онNoContext
чтобы определить, находится ли текущий процесс обновления в потоке событий React.
Как мы все знаем, когда React привязывает событие, он синтезирует событие и привязывает его кdocument
начальство(react@17
Изменено для привязки событий кrender
Элемент DOM, указанный в то время), наконец отправляется React.
Когда все события сработают, они будут вызываться первымиbatchedEventUpdates$1
Этот метод будет изменен здесьexecutionContext
значение, React знаетsetState
в их собственном контроле.
// executionContext 的默认状态
var executionContext = NoContext;
function batchedEventUpdates$1(fn, a) {
var prevExecutionContext = executionContext;
executionContext |= EventContext; // 修改状态
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
// 调用结束后,调用 flushSyncCallbackQueue
if (executionContext === NoContext) {
flushSyncCallbackQueue();
}
}
}
Итак, будь то прямой вызовflushSyncCallbackQueue
, или отложить вызов, здесь все синхронно по своей природе, но есть проблема последовательности.
В будущем будет асинхронный setState
Если вы внимательно посмотрите на приведенный выше код, вы обнаружите, что вscheduleUpdateOnFiber
метод, будем судитьlane
Это синхронно, тогда есть ли асинхронная ситуация?
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
if (lane === SyncLane) {
// 同步操作
ensureRootIsScheduled(root, eventTime);
// 判断当前是否还在 React 事件流中
// 如果不在,直接调用 flushSyncCallbackQueue 更新
if (executionContext === NoContext) {
flushSyncCallbackQueue();
}
} else {
// 异步操作
}
}
Когда два года назад React обновил свою волоконную архитектуру, он готовился к асинхронности. Он будет официально выпущен в React 18Concurrent
режим, оConcurrent
режим, официальное введение выглядит следующим образом.
Что такое параллельный режим?
Параллельный режим — это набор новых функций React, которые помогают приложениям оставаться отзывчивыми и соответствующим образом настраиваются в зависимости от производительности устройства пользователя и скорости Интернета. В параллельном режиме рендеринг не блокируется. Его можно прервать. Это улучшает пользовательский опыт. Он одновременно открывает новые возможности, которые раньше были невозможны.
Теперь, если вы хотите использоватьConcurrent
режим требует использования экспериментальной версии React. Если вам интересна эта часть, вы можете прочитать мою предыдущую статью:Эволюция архитектуры React — от синхронной к асинхронной.