Як браузер атрыбутуець кольоры і як у гэтым процесе выступае React
Дазвольце даклэ научыцца, як Critical Rendering Path, reconciliation, Fiber і Scheduler саўместна працуюць, каб ператварыць апдэйты React у пікселі на экране.
Спачатку, забудзіце пра React: як насправды браузер атрыбутуе сторанку?
На час адзінакоўце React. Нават просты HTML-документ з некалькама правілаў CSS праходзіць праз фіксаваную серыю крокаў, прычаму перш чым пазірнее хоць адзіны піксель, яшчэ не з’явіўся. Кожны браузер следуе гэтай серыі, незалежна ад таго, якімі інструментамі была створана сторанка:
HTML → DOM tree
CSS → CSSOM tree
DOM + CSSOM → Render Tree
Render Tree → Layout → Paint → Composite → Screen
Кожны этап выконвае конкрэтную задачу, і самыя назвы не даюць чыткага уявлення пра гэта:
- Дрэва DOM — браузер парсуе ваш HTML і ператварае яго на дрэва вузлаў. Це чыстая структура: якія элементы знаходзяцца внутры якіх.
- Дрэва CSSOM — тая ж ідея застосовуецца да вашых стыляў. Кожна правіла CSS, якую вы напісалі, ператвараецца на дрэва, якое браузер можа выкарыстоўваць для пошуку.
display: none застаецца ў DOM, але не включаецца ў дрэва адраджавання).Разам этыя крокі формуюць тое, што называецца Критычным шляхам адрадзівання, або CRP.
Гэтыя крокі выконваюцца аднакова незалежна ад таго, чыры вы используеце React, Vue, проста jQuery чы ўсёўсюды без бібліятэк. Адрадзіванне пікселей — це адпаведная задача браузера, а не чыёсь іншага фрэймворка.
Тады як жа React паслужыць у всім гэтым?
Этая частка выконваецца за момант пасля кліку. React не заменяе Крытычны шлях адрадзівання — ён працюе да яго.
Без React апдэйт UI значыць ручнае вышукванне правильнага DOM-вузла і його зміну самостайна:
const counterEl = document.getElementById('counter');
counterEl.textContent = newCount;
Гэта ўпорна для аднаго лічыльніка. Але уявіце панель керування, дзе чырвасць значэнняў можа змяніцца незалежна — вам даведзецца ручна стежыць за кожным з іх і апдэйтаваць іх. Самэ гэтае заведамства є прычыной існавання React.
За дапамою React тая ж самая апдэйтаванне выглядае так:
function Counter({ count }) {
return <p>{count}</p>;
}
Вы описваеце, як павінна выглядаць інтэрфейс на аднойчынку з дакументамі, і ніколі не прабуяце безпосередньа зменіць DOM. Таму калісь іншы элемент павінен выконваць гэту роботу. Гэта справжня задача React, і яна выконваная на стадіўцы раней, чым пачынаецца процес критычнай відраслівання браузера:
State changes → React figures out what changed → applies a small patch to the real DOM
↓
browser does its normal thing: Layout → Paint → Composite → Screen
Усё значэнне React заключаецца у тым, каб першы крок — выявленне змян — быў як магчыма шырэй і точней, тады браузер будзе пераробляць толькі тую частку стораніцы, якой гэта патрэбна, а не весь контент.
Імператыўны протыяк декларатыўнага: змена падходу
Гэты контраст пояснюе, чаму React быў спроектаваны такім чынам.
Імператыўны код пераказвае кожны окремы крок:
list.innerHTML = '';
for (const item of items) {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li);
}
Вы самі прымячете рашэнне: стерці гэта, стварыць той элемент, прыўязаць яго тут.
Декларатыўны код замест таго описвае рэзультат, які вы хачаце побачыць:
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
У замест на тое, каб паведаміць браузеру "стварыць li і дадзіць яго", вы заявляеце "з урахоўваннем гэтага масіву, такым должен быць выходны інтерфейс". Што-та інша павінна ператворыць гэты опис у конкретныя операцыі DOM — і наступны крок — это з’ясаванне, што саме гэта ўсё.
Што на самай працэ паводзіцца пад "супрацоўкай"
Гэты аспект здаецца складнейшым, чым ён на самай працэ, калі паглядзець на яго безпосередна.
React зберагае „ментальны фотакапсул“ вашай інтэрфейсной сістэмы, які называецца Virtual DOM. Калі ў чым-небудзь выходзяць змены, React стварае новую версію гэтага дрэва і порыўнюе яе з пярэдней, ўбачыць, што зменілася. Гэты крок порыўнення і є там, што люди называюць рэкансіляцыяй.
Пераглед кожнай можлівай разніцы между двума дрэвамі быў бы выключна трыцяжкі з комп’ютарнай точка зору, таму React выкорыстоўвае спрощэнне за дапамой двух правіл, якія спрабуюць заставіць працэс быстрым у рэальных умовах выкарыстання:
- Тып элемента зменіўся (напрыклад, `
` ператворыўся на ``) — React нават не адглядае дзецячыя элементы; ён проста адмовляецца ад старага вузла і стварае новы.
- Тып элемента застаўся тым самым (`
стае
`) — React заставляе існуючы рэальны вузел DOM і толькі прадаўляе тыя часткі, якія зменіліся.
Адзін з падчынных, які захопляюць практычна ўсіх, стосуецца спісаў. По значчыні стандарту React паруе элементы спіса за іх індэксамі — элемент 0 з елементам 0, элемент 1 з елементам 1 і так далей. Якща вы дадзеце новы элемент у пачатак спіса без ключа, React будзе прыпускать, што кожны элемент пад яму таксама зменіўся.
Самэй гэтага вы завжды должны прызначыць стабільны key элементам спіса:
{items.map(item => <li key={item.id}>{item.name}</li>)}
Калі элементы маюць ключ, React можа распазнаць, «гэты конкретны элемент толькі змяніў сваё месца», а не прыпускать, што весь спіс быў перзбудаваны.
Пасля всіх гэтых параванняў React атрымлея коракі, спрыяваныя конкрэтным задачам — такія як «ацюнаваць гэты тэкстовы вузол» або «аддаць вузел тут» — і самэ гэтыя операцыі фактычна прыменяюцца да рэальнага DOM.
Разве гэта не тое ж самае, што робіць Fiber?
Это справедлівы вопыт, які варта рассмотрзець.
Основная ідея — супрацоўка. Fiber ёсць проста інструмент, які яе рэалізуе.
Да выходу React 16 гэты інструмент называўся Stack Reconciler. Ён пераскакваў усю структуру рекурсыўна і сінхронна, тое значыць, як толькі ён пачынаў працаваць, яго трэба было завершыць перад тым, как зупініць. Пры большых апдэтах гэта могла зайняць главную вясь на столькі часу, што прыстрой стаў поволі работаць — кадры моглі з’являцца рэдка, а набіранне тексту — стаць неадекватным.
Fiber, які быў уведзен у React 16, замяніў тую систему. Канцэпцыя супрацоўкі з несувязамі не змянілася, але тепер робота дзеліцца на маленькія фрагменты, якія можна паставіць на паузу, адмовіцца ад іх чысткі або пракрануць далей. Якщо з’являецца ўжо болей трыбутны задача — напрыклад, калі корыстнік пачынае пісаць — React можа перарваць роботу над менш трыбутнымі задачамі, адразу зайсціся трыбутной актуалізацыяй, а потым вернуцца да таго, дзе застаў.
Таму некоректна сказваць, што Fiber замяніў механізм супрацоўкі з несувязамі. Болей правядлівае сказаць, што старыя механізмы, якіе виконвалі такую супрацоўку, былі замянены на болей супэльныя.
Што ж робіць Планавальнік?
Fiber робіць магчымым паставіць на паузу і продаважыць роботу, але трэба, каб іншы элемент вярнуўся за тым, колі паставіць на паузу і каторая задача заслуговае на прыорітэт. Гэта і є ролю Планавальніка.
Яго абавясці включаюць:
- Адаптаванне рангавання па ступені трыбоцеўнасці — напрыклад, обработка натыку клавішы ў поле вводу лягчае атрымлівае статус трыбоцеўнасці, тады калі апдэйт далёкага фонавога списку такі статус не мае.
- Падпіленне прастору межаў канструкцыйяў браузера, ў результате чаго можна поступова выкананыя працы з ніжшай прыоритэтнасцю, а таксама можна зупініцца перад тым, як патрэбна будзе адобразіць наступную канструкцыю.
- Актываўванне можлівасцяў React 18, такіх як
startTransition; пазначэнне апдэйту як нетрыбоцеўнасці фактычна паведамляе механізм планавання, што можна перашчыльнуць гэтую працу на ніжшы ранг у списку прыоритэтаў.
Простая ментальная модэль спаўнае гэтыя тры паняціі:
Reconciliation → the algorithm (what changed?)
Fiber → the engine that makes that algorithm interruptible
Scheduler → the traffic controller deciding when to pause/resume Fiber's work
Этап адобразэння проты этапа фіксацыі
Є ўсё ж адна яшчэ разліка, якую варта зрозумець: Fiber дзеліць свою працу на два этапы, якія паводзяцца за аблічна разнымі правіламі.
Этап выканання — гэта тады, калі фактычна парадоксаванне ведзецца. React вызывае функцыі вашага компонента, складае новую структуру і порыхвае яе з пярэдней. Нічога з гэтаго яшчэ не паўтараецца на рэальнай сторанцы, і самэльга гэты этап можна без апасцярыбы націснуць, адмовіцца ад яго выканання чыста запусціць зноў.
Этап зафіксавання — гэта тады, калі React нарэшце запісвае змяны у рэальны DOM і прыкладзае вырахаваны патч. Гэты этап нельга перарваць — ён выкананы цэлком і без перерываў, таму што частковая адмена інтэрфейса заставіла бы сторанцу ў несправным візуальным стане. Адразу пасля адмены DOM, але перад тым, як браузер намалюе экран, useLayoutEffect запускаецца сінхронна. useEffect, на адміну, выкананы чырвоныя пазней, пасля таго, як браузер вяршыў малюванне.
Чы працоўна вам ўзагалі патрэбны React?
Чыста правда, не завжоды. Большая канцэлка веб-сайтаў працуюць толькі на HTML, CSS і звычным JavaScript, і ўсё працуе чыста.
React пачынае правядзіць ся сваёй додатковай навантажэнням, калі вашы трэбованні стаюць болей складнымі:
- Ручная налагодка апдэйтаў DOM ёсць прыемна для маленькіх проектаў, але становіцца неконтрольным, калі трэба кераваць дзесяткамі взаімазалежных элементаў інтерфейсу.
- Значны частака багоў у рэальных інтерфейсах выходзіць з таго, што стан і адобразжаны інтерфейс не супараднуюцца адно з другім. Падход React — спрацоўваць інтерфейс як функцыю стану і дазволіць фрэймворку кераваць разлікамі — з моменту стварэння усуняе вельмі частку такога рызыка.
- Магчымасць ствараць перадавальныя компаненты, падтрымваныя екасістэмай інструментаў для маршрутацыі, інструментаў разработчика і спаканаваных стандартаў, становіцца цэнным, калі пра адной і той жа базе коду працуюць больш за аднаго разработчыка.
Для простай старонкі або сайту, які ў большай частцы статычныя, просты JavaScript яўляецца лепшым выборам. Выварот Fiber, Scheduler і цэлага пайплайна для сінхронізацыі значыць неабходнасць платы за дадатковыі ресурс для проблэмы, якой на самай працоўцы не было.
React не ёсць прыродна вышэйшым за JavaScript. Це набор інструментаў, створаны для рашэння адной конкрэтнай проблэмы: падтрымкі сінхронізацыі інтерфейсу з станам, які постаўляецца зменяецца столькі часу, у масштабных проектах і ў команде. Пад такім масштабам просты HTML, CSS і JS чыста адмініструюць задачу на высокай якосці.
Спаднёе чытанне
- Усуненне перавантажэння пропамі ў React за дапамою композыцыі і слотаў — Дазвольце дазнацца, чаму пропы React з вялікай канфігурацыёй ствараюць проблемы з адтрымкай, і як інверсія кантролю, композыцыя і слоты дапамагаюць ствараць справжна перадавальныя компоненты.