Введение
Сегодня давайте поговорим о текущей ситуации и будущем асинхронных компонентов в React.Асинхронные компоненты, вероятно, станут гладким техническим решением от взаимодействия с данными до отображения пользовательского интерфейса в будущем, поэтому, поскольку вы хотите досконально понять React и продвинуть React, это необходимо понимать асинхронные компоненты.
Старые правила, мы все еще начинаем сегодняшнее мышление с вопросов? (Самопроверка мастерства)
- 1 Что такое асинхронные компоненты React и какие проблемы они решают?
- 2 Как componentDidCatch отлавливает ошибки этапа рендеринга и компенсирует их.
- 3 Как React.lazy обеспечивает динамическую загрузку?
- 4 Почему React.lazy находится внутри Supsonse.
- 5 Каков принцип Supsonse?
(исходная версия v16.13.1)
Два первых знакомства: асинхронные компоненты
1 Что такое асинхронный компонент
Давайте сначала подумаем о текущем приложении React, используяajax
илиfetch
Сценарий взаимодействия с данными в основном такой, в компоненте классаcomponentDidMount
и функциональные компонентыeffect
Взаимодействие с данными выполняется в пользовательском интерфейсе, и после получения данных отображается представление пользовательского интерфейса. Так может ли рендеринг компонента ждать завершения асинхронного запроса данных, а затем рендерить его после получения данных?
Для вышеописанной ситуации первое ощущение невероятное: что, если рендеринг можно прервать и дождаться рендеринга после запроса данных? То естьSusponse
, невозможные вещи, упомянутые выше,Susponse
Так и было, добавил React 16.6,Susponse
Заставьте компонент «ждать» асинхронной операции, пока асинхронная операция не завершится для рендеринга.
Традиционный режим: визуализация компонента -> запрос данных -> повторная визуализация компонента.
Асинхронный режим: запрос данных -> компонент рендеринга.
2 Включите режим ожидания
Взаимодействие данных в традиционном режиме должно выглядеть так.
function Index(){
const [ userInfo , setUserInfo ] = React.useState(0)
React.useEffect(()=>{
/* 请求数据交互 */
getUserInfo().then(res=>{
setUserInfo(res)
})
},[])
return <div>
<h1>{userInfo.name}</h1>;
</div>
}
export default function Home(){
return <div>
<Index />
</div>
}
- Процесс: монтирование инициализации страницы,
useEffect
Внутренние данные запроса, черезuseState
Измените данные и дважды обновите данные рендеринга компонента.
тогда, если вы используетеSusponse
Асинхронный режим можно записать так:
function FutureAsyncComponent (){
const userInfo = getUserInfo()
return <div>
<h1>{userInfo.name}</h1>;
</div>
}
/* 未来的异步模式 */
export default function Home(){
return <div>
<React.Suspense fallback={ <div > loading... </div> } >
<FutureAsyncComponent/>
</React.Suspense>
</div>
}
Когда данные не были загружены, они будут отображатьсяSuspense
серединаfallback
контент, чтобы компенсировать эффект перехода в данных запроса.Хотя этот режим официально не доступен в текущей версии, React будет поддерживать эту форму кода в будущем.
Три возможности отслеживания: от componentDidCatch до Suspense
Что касается того, как Suspense делает невозможное возможным? Это изcomponentDidCatch
Кстати говоря, когда React запустил v16, была добавлена новая функция жизненного цикла.componentDidCatch
. Если компонент определяетcomponentDidCatch
, то когда все дочерние компоненты в этом компоненте выдают исключение во время рендеринга, этоcomponentDidCatch
будет вызвана функция.
использование компонентаDidCatch
componentDidCatch может ловить исключения, он принимает два параметра:
- 1 ошибка - выброшена ошибка.
- 2 info — объект с ключом componentStack, который содержит информацию стека об ошибках, вызванных компонентом.
Давайте смоделируем ситуацию, когда дочерний компонент не отрисовывается:
/* 正常组件,可以渲染 */
function Children(){
return <div> hello ,let us learn React </div>
}
/* 非React组件,将无法正常渲染 */
function Children1(){
return
}
export default class Index extends React.Component{
componentDidCatch(error,info){
console.log(error,info)
}
render(){
return <div>
<Children />
<Children1/>
</div>
}
}
Как и выше, мы имитируем сценарий сбоя рендеринга и визуализируем компонент Children1, не являющийся React, как обычный компонент React, чтобы на этапе рендеринга сообщалось об ошибке и отображалось сообщение об ошибке.componentDidCatch
Захвачено, сообщение об ошибке выглядит следующим образом:
Для вышеизложенного, если при рендеринге дочернего компонента возникает ошибка, это приведет к сбою рендеринга всего компонента и невозможности его отображения.Children
также будет замешан, в это время нам нужноcomponentDidCatch
сделать некоторые средства, как мы нашлиcomponentDidCatch
не получится, могу датьChildren1
Добавьте элемент управления состоянием, который прекращает работу в случае сбоя рендеринга.Children1
оказывать.
function ErroMessage(){
return <div>渲染出现错误~</div>
}
export default class Index extends React.Component{
state={ errorRender:false }
componentDidCatch(error,info){
/* 补救措施 */
this.setState({
errorRender:true
})
}
render(){
return <div>
<Children />
{ this.state.errorRender ? <ErroMessage/> : <Children1/> }
</div>
}
}
Если возникает ошибка, перейдитеsetState
Повторно визуализируйте и удалите неисправный компонент, чтобы компонент мог нормально визуализироваться, не затрагивая монтирование дочерних элементов.componentDidCatch
С одной стороны, ошибки, возникающие на этапе рендеринга, могут быть зафиксированы, а с другой стороны, побочные эффекты могут быть выполнены в течение жизненного цикла для восстановления потерь, вызванных исключениями рендеринга.
принцип componentDidCatch
componentDidCatch
Принцип должен быть хорошо понят, и внутреннее может быть пропущено черезtry{}catch(error){}
для обнаружения ошибок рендеринга и обработки ошибок рендеринга.
try {
//尝试渲染子组件
} catch (error) {
// 出现错误,componentDidCatch被调用,
}
Можно ли перенести идею componentDidCatch в Suspense?
Затем вернемся к нашим асинхронным компонентам.Если асинхронный код выполняется синхронно, то он точно не будет нормально отображаться.Нам все равно нужно сначала запросить данные.ЖдатьКогда данные возвращаются, а затем используют возвращенные данные для рендеринга, то основное внимание уделяется этомуЖдатьВорд, как остановить синхронный рендеринг и дождаться асинхронного запроса данных? Можно ли генерировать исключения? Исключения могут остановить выполнение кода и, конечно же, рендеринг.
Suspense
Это рендеринг, который прерывается выдачей исключения.Suspense
нужен одинcreateFetcher
Функция инкапсулирует асинхронную операцию при попыткеcreateFetcher
При чтении данных с возвращаемым результатом возможны две возможности: первая — данные готовы, тогда результат возвращается напрямую; другая — асинхронная операция не закончилась и данные не готовы в это время.createFetcher
выдаст "исключение".
Является ли это «исключение» обычной ошибкой кода? Нет, это исключение представляет собой объект Promise, который инкапсулирует данные запроса, и это настоящий метод запроса данных.componentDidCatch
изtry{}catch{}
чтобы получить это исключение.
Что делать после получения этого исключения?Мы знаем, что это исключениеPromise
, то, конечно, следующим шагом будет выполнение этогоPromise
, в успешном состоянии получить данные, а затем снова визуализировать компонент, рендеринг в это время считывает обычные данные, тогда рендеринг может выполняться в обычном режиме. Далее мы моделируемcreateFetcher
а такжеSuspense
Мы моделируем простой createFetcher
/**
*
* @param {*} fn 我们请求数据交互的函数,返回一个数据请求的Promise
*/
function createFetcher(fn){
const fetcher = {
status:'pedding',
result:null,
p:null
}
return function (){
const getDataPromise = fn()
fetcher.p = getDataPromise
getDataPromise.then(result=>{ /* 成功获取数据 */
fetcher.result = result
fetcher.status = 'resolve'
})
if(fetcher.status === 'pedding'){ /* 第一次执行中断渲染,第二次 */
throw fetcher
}
/* 第二次执行 */
if(fetcher.status)
return fetcher.result
}
}
- Возвращает функцию, выполненную на этапе рендеринга, при первом рендеринге компонента из-за
status = pedding
Поэтому выбрасывается исключениеfetcher
ДатьSusponse
, рендеринг прерван. -
Susponse
будет внутриcomponentDidCatch
Чтобы обработать этот сборщик, выполнитеgetDataPromise.then
, В настоящее времяstatus
ужеresolve
статус, данные также могут быть возвращены в обычном режиме. - следующий
Susponse
Визуализируйте компонент снова, в это время вы можете получить данные в обычном режиме.
Мы моделируем простой Suspense
export class Suspense extends React.Component{
state={ isRender: true }
componentDidCatch(e){
/* 异步请求中,渲染 fallback */
this.setState({ isRender:false })
const { p } = e
Promise.resolve(p).then(()=>{
/* 数据请求后,渲染真实组件 */
this.setState({ isRender:true })
})
}
render(){
const { isRender } = this.state
const { children , fallback } = this.props
return isRender ? children : fallback
}
}
- Используйте componentDidCatch для захвата асинхронных запросов.Если есть асинхронный запрос на резервную визуализацию, дождитесь завершения выполнения асинхронного запроса, а затем визуализируйте настоящий компонент, чтобы завершить весь асинхронный процесс. Но для того, чтобы все поняли процесс, это всего лишь симуляция асинхронного процесса, а реальный процесс намного сложнее этого.
блок-схема:
4 Практика: от саспенса к React.lazy
Введение в React.lazy
Suspense
Революция, вызванная асинхронными компонентами, еще не дала существенного результата, и текущая версия официально не введена в действие, ноReact.lazyЭто лучшая практика для текущей версии Suspense. мы все знаемReact.lazy
СотрудничатьSuspense
Можно реализовать ленивую загрузку и загрузку по требованию, что очень способствует сегментации кода и не будет загружать большое количество файлов во время инициализации, сокращая время на первом экране.
Основное использование React.lazy
const LazyComponent = React.lazy(()=>import('./text'))
React.lazy
Принимает функцию, которую необходимо вызвать динамическиimport()
. он должен вернутьPromise
,ДолженPromise
нужноresolve
Одинdefault export
изReact
компоненты.
Давайте сначала посмотрим на основное использование:
const LazyComponent = React.lazy(() => import('./test.js'))
export default function Index(){
return <Suspense fallback={<div>loading...</div>} >
<LazyComponent />
</Suspense>
}
мы используемPromise
имитировать этоimport()
Эффект будет как вышеLazyComponent
Измените его, чтобы он выглядел так:
function Test(){
return <div className="demo" >《React进阶实践指南》即将上线~</div>
}
const LazyComponent = React.lazy(()=> new Promise((resolve)=>{
setTimeout(()=>{
resolve({
default: ()=> <Test />
})
},2000)
}))
Эффект:
Интерпретация принципа React.lazy
Как React.lazy работает с Susponse для достижения динамической загрузки?На самом деле, createFetcher сделан внутри lazy, и вышеупомянутый createFetcher получает отрендеренные данные, а createFetcher, который идет с lazy, асинхронно запрашивает компоненты. lazy внутренне имитирует сценарий спецификации promiseA. Мы можем полностью понять, что React.lazy использует Promise для имитации процесса запроса данных, но результатом запроса являются не данные, а динамический компонент.
Далее, давайте посмотрим, как обрабатывается lazy
react/src/ReactLazy.js
function lazy(ctor){
return {
$$typeof: REACT_LAZY_TYPE,
_payload:{
_status: -1, //初始化状态
_result: ctor,
},
_init:function(payload){
if(payload._status===-1){ /* 第一次执行会走这里 */
const ctor = payload._result;
const thenable = ctor();
payload._status = Pending;
payload._result = thenable;
thenable.then((moduleObject)=>{
const defaultExport = moduleObject.default;
resolved._status = Resolved; // 1 成功状态
resolved._result = defaultExport;/* defaultExport 为我们动态加载的组件本身 */
})
}
if(payload._status === Resolved){ // 成功状态
return payload._result;
}
else { //第一次会抛出Promise异常给Suspense
throw payload._result;
}
}
}
}
-
React.lazy
Обернутые компоненты отмеченыREACT_LAZY_TYPE
элемент типа, который на этапе согласования становитсяLazyComponent
Типfiber
,React
правильноLazyComponent
Будет отдельная логика обработки, первый рендеринг будет выполняться первым_init
метод. В это время это_init
метод можно понимать какcreateFetcher
.
Давайте посмотрим на выполнение функции инициализации в ленивом режиме:
react-reconciler/src/ReactFiberBeginWork.js
function mountLazyComponent(){
const init = lazyComponent._init;
let Component = init(payload);
}
-
как указано выше
mountLazyComponent
Выполняется во время инициализации_init
метод, который будет выполнятьсяlazy
Первая функция , получаетPromise
, связыватьPromise.then
Успешный обратный вызов, мы получаем наш компонент в обратном вызовеdefaultExport
, здесь следует отметить, что когда вышеприведенная функция оценивается по второму if, потому что состояние неResolved
, так пойдетelse
, выдает исключение Promise, выдача исключения приведет к прекращению текущего рендеринга. -
Susponse
справиться с этим внутриpromise
, а затем снова визуализировать компонент, а при следующем рендеринге компонент будет визуализироваться напрямую. достичь цели динамической загрузки.
блок-схема
5 Перспективы: будущее саспенса можно ожидать
В настоящее время вы не используете Relay, поэтому пока не можете попробовать Suspense в своем приложении. Потому что пока что из библиотек, реализующих Suspense, Relay — единственная, которую мы протестировали в продакшене и уверены в ее работе.
Задержка в настоящее время недоступна. Если вы хотите использовать ее, вы можете попробовать использовать интеграцию в производственной среде.Suspense
изRelay
.Руководство по реле!
Что может решить саспенс?
-
Suspense
Пусть библиотека выборки данных работает сReact
тесно интегрированы. Если библиотека запросов данных реализуетSuspense
поддержку, то вReact
используется вSuspense
будет естественным. -
Suspense
Можно свободно отображать эффект загрузки в запросе. Позволяет более активно контролировать загрузку представления. -
Suspense
Это может сделать данные запроса для рендеринга более плавными и гибкими, нам не нужноcomponentDidMount
Запросите данные, снова запустите рендеринг и передайте всеSuspense
Решите ее за один раз.
Приостановка перед вызовами?
на будущееSuspense
Можно ли использовать его в качестве основного решения для рендеринга данных асинхронных запросов, автор считает, что Suspense по-прежнему полон ожиданий в будущем, поэтому, по моему личному мнению, вызов Suspense заключается в следующих аспектах:
-
1
concurrent
в режимеSusponse
Это может улучшить пользовательский опыт, а команда реагирования может сделать будущее приостановку более гибким, с более четким наборомcreateFetcher
Изготовление руководств — это будущееconcurrent
в режимеSuspense
Ключ к тому, чтобы выделиться. -
2
Suspense
Возможность широкого использования зависит отSuspense
экологическое развитие, имеется стабильная библиотека запросов данных сSuspense
Идеально подходит. -
3 пары разработчиков
Suspense
признание стоимости, еслиSuspense
Если в будущем производительность улучшится, будет больше разработчиков, которые предпочтут сами инкапсулировать набор методов запроса данных и дать отличныеSuspense
заказ на покупку.
Шесть резюме
В этой статье рассказывается о происхождении React Susponse, принципе его реализации, его текущем состоянии и перспективах на будущее.Что вы думаете о прошлой и настоящей жизни React?Вы можете написать свое мнение в области комментариев или указать на ошибки автора.
Я написал буклет для углубленного систематического изучения React
Чтобы каждый мог систематически изучать React и продвигать его, автор недавно написал буклет «Руководство по расширенной практике React».Базовый Расширенный,Оптимизация и расширенные статьи,Расширенные принципы,Продвинутая экология,Продвинутая практика, пять направлений, чтобы подробно обсудить руководство по использованию React и введение в принципы.
-
существуетБазовый РасширенныйЗдесь мы заново поймем состояние, свойства, ссылку, контекст и другие модули в реакции и подробно объясним их основное использование и игровой процесс высокого уровня.
-
существуетОптимизация и расширенные статьиВ этом уроке мы расскажем о настройке производительности React и подробной обработке, чтобы React можно было написать более элегантно.
-
существуетРасширенные принципыВ этой статье я подробно расскажу о принципах нескольких основных модулей React и решу проблемы принципов React в разовых интервью.
-
существуетПродвинутая экологияЗдесь мы рассмотрим использование ключевой экологии React и проанализируем внутренний механизм работы с принципиальной точки зрения.
-
существуетПродвинутая практикаЗдесь первые несколько модулей будут соединены последовательно для интенсивной практики.
Что касается того, почему буклет называется Advanced Practice Guide, потому что, объясняя расширенный игровой процесс, он также содержит множество небольших демонстраций для практики. В интервью также есть несколько сессий вопросов и ответов, чтобы читатели выделялись из интервью.
В настоящее время и после завершения 20 глав буклет будет выпущен в ближайшее время.Те, кто хочет продвигать React, могут зайти сюда и обратить внимание на последние новости буклета.
Напоследок подарите розы и оставьте в руках стойкий аромат.Друзья, которые чувствуют, что что-то приобрели, могут подарить авторуНравится, следуйОдна волна, обновляйте фронтальные суперхардкорные статьи одну за другой.