Size: a a a

Иван Акулов про разработку

2021 March 19
Иван Акулов про разработку
4️⃣ Web Vitals Report

web-vitals-report.web.app строит отчёт по Core Web Vitals на основании вашей гугл-аналитики. (Правда, эти данные нужно в аналитику сначала залить — с помощью пакета web-vitals.)

Это единственный тул, который я ещё не пробовал (не собираю Core Web Vitals в аналитику). Но некоторым клиентам он пригождается
источник
Иван Акулов про разработку
5️⃣ Data Studio

А вот если вы не собираете данные в аналитику, попробуйте сделать вот такой отчёт: https://datastudiohelp.com/core-web-vitals-in-data-studio-using-the-crux-dashboard/

Открываете шаблон → клонируете к себе, указывая новый адрес сайта → получаете огромный перформанс-отчёт.

Внутри
— Core Web Vitals
— другие метрики типа Time to First Byte или First Paint
— и исторические данные
источник
Иван Акулов про разработку
6️⃣ useWhyDidYouUpdate

usehooks.com/useWhyDidYouUpdate — это Реакт-хук, который печатает, почему какой-то компонент перерисовался. Его даже не надо устанавливать — просто скопируйте и вставьте 👌

Идеально работает, чтобы продебажить перформанс одного компонента. И подключается гораздо проще, чем why-did-you-render
источник
Иван Акулов про разработку
7️⃣ Font Style Matcher

meowni.ca/font-style-matcher помогает найти фолбэк-шрифт, который похож по размеру на кастомный.

Если на сайте подключён шрифт Merriweather, а фолбэком стоит Georgia (которая визуально меньше), то, когда Merriweather загрузится, страница скакнёт. Из-за этого получится плохой Cumulative Layout Shift.

А вот Font Style Matcher поможет подобрать такой размер Georgia, который максимально похож на Merriweather, и убрать скачки страницы 💅
источник
Иван Акулов про разработку
Расскажите про ещё какие-то тулы, которые вы знаете?
источник
2021 April 22
Иван Акулов про разработку
​​📉 Есть один перформанс-антипаттерн в Реакте, который я встречаю у клиентов.

Пример
Представьте, что есть две функции, которые мы хотим передать в контекст:

function signIn() { ... }

function signOut() { ... }


Как передать их в контекст? Проще всего — вот так:
источник
Иван Акулов про разработку
​​Антипаттерн
И вот тут есть проблема. В этом примере value контекста — это объект { signIn, signOut }.

Этот объект создаётся заново каждый раз, когда вызывается AuthWrapper. Из-за этого контекст каждый раз получает новое значение. А при изменении контекста React перерисовывает всё, что подключено к этому контексту.

В итоге каждый раз, когда мы перерисовываем AuthWrapper, React перерисовывает ещё и все зависимые от контекста компоненты. Если компонентов много, это особенно дорого.

shouldComponentUpdate/PureComponent/React.memo() не помогают
React.memo(), PureComponent и shouldComponentUpdate предотвращают перерисовку компонента, если его пропсы и стейт не поменялись. Но они не работают с контекстом; поэтому тут они бессильны.

Как чинить
Чтобы починить, нужно сделать, чтобы объект не менялся каждый раз.

1) Самый простой способ — это вынести его из компонента:
источник
Иван Акулов про разработку
​​2) Иногда это невозможно — например, когда signIn и signOut приходят из пропсов. Тогда оберните объект в useMemo

Главное — проследите, чтобы signIn или signOut тоже не менялись при каждой перерисовке. А то useMemo будет каждый раз перевычисляться, и объект всё равно будет разным.
источник
Иван Акулов про разработку
​​useContextSelector

В комментариях рассказали про библиотеку useContextSelector. Это ещё одно решение проблемы: вместо подписки на весь контекст можно подписаться только на нужное поле ↓

useContextSelector скоро, похоже, появится и в самом React-е: RFC · пулл-реквест
источник
2021 July 12
Иван Акулов про разработку
styled-components 💅

В твитере любят писать, что styled-components медленные, но обычно не приводят никаких примеров. Вот вам пара кейсов из практики.
источник
Иван Акулов про разработку
Всплывающее меню
Вот, например, есть у вас страничка с 500 строками в таблице:
источник
Иван Акулов про разработку
источник
Иван Акулов про разработку
И у каждой строки есть кнопка меню:
источник
Иван Акулов про разработку
источник
Иван Акулов про разработку
Каждое меню обёрнуто в компонент, который его позиционирует:

const MenuWrapper = styled.div`
 position: absolute;
 top: ${props => props.offsetTop || DEFAULT_OFFSET_TOP}px;
 left: ${props => props.offsetLeft || DEFAULT_OFFSET_LEFT}px;
 z-index: 10;
`


Если на странице достаточно элементов, вы заметите, что когда вы открываете меню, страница зависает на полсекунды. Причём зависает не каждый раз, а только когда меню открывается в первый раз.
источник
Иван Акулов про разработку
Почему так происходит? Когда вы монтируете MenuWrapper в первый раз, styled-components генерирует для него новый класс со всеми стилями:

<MenuWrapper top={100} left={500} />
// → .dBJeRo { position: absolute; top: 100px; left: 500px; z-index: 10; }

и присваивает этот класс DOM-элементу MenuWrapper-а:

// → <div class="MenuWrapper-ptr1yu-0 dBJeRo">...</div>

Потом, когда вы закроете меню, и MenuWrapper отмонтируется, этот класс останется в документе. Когда вы откроете меню во второй раз, styled-components применит его снова.
источник
Иван Акулов про разработку
Проблема вот в чём. Если вы смонтируете MenuWrapper с другими пропами, styled-components сгенерирует для него другой класс:

<MenuWrapper top={200} left={500} />
// → .ir7ERf { position: absolute; top: 200px; left: 500px; z-index: 10; }


На страничке 500 меню. Каждое меню обёрнуто MenuWrapper-ом со своими пропами. Каждый раз, когда вы открываете новое меню, вы монтируете новый уникальный MenuWrapper. Это заставляет styled-components сгенерировать для него новый класс.

А это дорого. Когда на странице меняется CSS, браузер вынужден перевычислить стили и лэйаут для всех элементов страницы. Если элементов много, это может занять и полсекунды.

***
источник
Иван Акулов про разработку
createGlobalStyle
styled-components позволяют писать глобальные стили. Например, если вам нужно застайлить popover из Bootstrap, это делается вот так:

const BootstrapPopoverOverrides = createGlobalStyle`
 .popover {
   background: #efefef;
   z-index: 999;
 }
`

const Popover = ({ menuItems }) => {
 return <>
   <BootstrapPopoverOverrides />
   …
 </>
}

Если сделать такой поповер, поместить его на страницу с кучей элементов и попробовать пооткрывать его, страница будет зависать. Причём, в отличие от прошлого примера, зависать страница будет и тогда, когда поповер закрывается.
источник
Иван Акулов про разработку
Почему так происходит? Потому что, когда вы монтируете BootstrapPopoverOverrides, styled-components добавляет в документ новый блок стилей:

<BootstrapPopoverOverrides />
// → <style>.popover { background: #efefef; z-index: 999; }</style>

Потом, когда вы удаляете BootstrapPopoverOverrides из документа, styled-components удаляет это блок. И так каждый раз, когда поповер открывается или закрывается.

А перевычисление стилей и лэйаута — это дорого. Если элементов на странице много, это может занять и полсекунды.

***
источник
Иван Акулов про разработку
В чём проблема
Короче говоря, проблема со styled-components не в том, что они тормозят сами по себе, а в том, что они медленные в неожиданные моменты времени. То есть приложение не станет медленным, если добавить в него styled-component:
источник