Ви витратили місяці або навіть роки, створюючи застосунок для вашої організації. Все чудово, виснажливий процес тестування позаду, до того ж ви отримали хорошу оцінку на етапі бета-тестування.
Настав той самий день запуску застосунку, коли ви запитуєте себе: чи витримає мій застосунок несподівані випробування та надмірне навантаження?
Під час розробки ПЗ часто розв'язання проблем продуктивності відкладається на останній момент (коли вже пізно щось вирішувати). Однак є інструменти, що допомагають вчасно виявити слабкі місця у продуктивності та покращити цю ситуацію ще на ранніх етапах розробки.
Зі зростанням застосунку ми починаємо помічати, як страждає наша анімація, а також продуктивність рендерингу компонентів.
Припустимо, що ви займаєтесь розробкою застосунку, де є чат з декількома каналами, до яких можна приєднатися. Зі зростанням кількості повідомлень в кожному каналі (їх стає сотні або навіть тисячі) ми помітимо, як страждає скролінг та перемикання між каналами.
Тож у цій статті ми розглянемо інструменти та методи, які допоможуть виявити та уникнути зайвих рендерів у вашому застосунку на React.
Варто зазначити, що React використовує багато технік, аби звести кількість DOM-операцій до мінімуму. Для багатьох застосунків використання production build достатньо, щоб задовольнити або навіть перевершити свої очікування щодо продуктивності. Та є способи зробити цей показник ще кращим.
Виявлення проблем з продуктивністю
В react-dom
16.5+ команда React передбачила розширені можливості використання поліфілів в режимі dev через React DevTools. Саме до цих інструментів варто звертатися найперше, щоб оптимізувати продуктивність:
1. React DevTools Profiler
Якщо ви відчуваєте, що проблема в певному компоненті, то перший інструмент, який стане у пригоді — DevTools Profiler. Його використання для отримання даних про продуктивність детально описане у статті та відео.
2. Виділення оновлень в React DevTools
React підтримує віртуальний DOM, котрий допомагає визначити, які частини UI необхідно повторно рендерити — залежно від props чи стану. Це, звичайно, чудово, але ми не можемо насправді дізнатися, які частини нашого застосунку оновлюються в певний момент. В React DevTools є опція, яка виділяє на екрані елементи під час їх рендерингу (початкового чи повторного).
Як можна побачити на прикладі форми, коли ми вводимо дані в поле вводу, отримуємо дуже багато зайвих рендерів. Далі ми розберемося, як можна уникнути такої ситуації.
3. Why Did You Render
Отже, ми знаємо, що наш застосунок повторно рендериться частіше, ніж це потрібно. Як же виявити основну причину такої поведінки? На щастя, існує чудова утиліта @welldone-software/why-did-you-render
, яка повідомить про небажані повторні рендери.
Налаштувавши why-did-you-render
, ви знайдете у вашій консолі інформацію, що допоможе відстежити, коли і чому певні компоненти рендеряться повторно (а також поширені варіанти розв'язання проблеми). Протестувати бібліотеку можна за посиланням.
Боремось з поширеними проблемами продуктивності
Після того як ми виявили потенційні слабкі місця у продуктивності застосунку, ми можемо побачити декілька поширених проблем і уникнути їх.
Уникаємо звірки
Як вже було зазначено, React внутрішньо представляє ваш застосунок у вигляді віртуального DOM, а далі робить з ним звірку. Кожного разу, коли стан, props чи батьківський компонент оновлюється, він спричиняє оновлення усіх залежних компонентів.
Якщо ви налаштували why-did-you-render
, то могли побачити декілька повідомлень про оновлення компонентів при зміні посилань props/state, при тому, що значення залишаються незмінними. Це хороший приклад ситуації, коли ми можемо вказати React не рендерити компонент повторно, тому що результат не зміниться.
Для цього є три методи:
-
shouldComponentUpdate
-
React.PureComponent
-
React.memo
Аналізуємо передачу props
Якщо заглибитись в поняття незмінності та процес передачі props, можна виявити, що саме тут криється помітний вплив на продуктивність. Ви можете здивуватись, але якщо ви створите подібний компонент, він буде постійно повторно рендеритись.
function App (items) {
return (
<BigListComponent
style={{width: '100%'}}
items={items}
/>
);
}
Щоб уникнути такої ситуації, варто оголосити змінну зі значенням стилю поза методом render
.
const bigListStyle = { width: '100%' };
function App (items) {
return (
<BigListComponent
style={bigListStyle}
items={items}
/>
);
}
Та сама ситуація, коли ми передаємо вбудовану функцію як prop:
// ПОГАНО: Вбудована функція
function App (items) {
return (
<BigListComponent
onClick={() => dispatchEvent()}
/>
);
}
// ДОБРЕ: Посилання на функцію
const clickHandler = () => dispatchEvent();
function App (items) {
return (
<BigListComponent
onClick={clickHandler}
/>
);
}
shouldComponentUpdate
З методом життєвого циклу shouldComponentUpdate
ви могли ознайомитись, коли вперше вчили React. Метод запускається перед процесом повторного рендерингу. Якщо він повертає true
, React покаже компонент повторно. В іншому випадку компонент не буде повторно рендеритись.
shouldComponentUpdate(nextProps, nextState) {
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
React.PureComponent
Замість того щоб використовувати shouldComponentUpdate
, ви зазвичай можете віднаслідувати ваш компонент від React.PureComponent
. Під капотом він робить поверхневе порівняння поточного/попереднього стану props.
class Button extends React.PureComponent {
...
}
React.memo
React.memo
передбачає той самий функціонал, однак у вигляді функціонального компонента, замість компонента на основі класу.
function Button (props) {
/* ... */
}
function areEqual (prevProps, nextProps) {
return prevProps.count === nextProps.count;
}
export default React.memo(Button, areEqual)
Віртуалізація довгих списків
Щоб розв'язати проблему з компонентами на зразок стрічки чату, рекомендується підхід windowing. Передбачається, що ви відображаєте тільки ту частину списку, яку бачить користувач (+/- заданий зсув), щоб зменшити час на рендеринг. Коли користувач буде скролити, нові елементи будуть підвантажуватись та рендеритись. Таку техніку вже реалізують бібліотеки react-window
та react-virtualized
.
Ще немає коментарів