Оптимізуємо продуктивність React-застосунків

5 хв. читання

Ви витратили місяці або навіть роки, створюючи застосунок для вашої організації. Все чудово, виснажливий процес тестування позаду, до того ж ви отримали хорошу оцінку на етапі бета-тестування.

Настав той самий день запуску застосунку, коли ви запитуєте себе: чи витримає мій застосунок несподівані випробування та надмірне навантаження?

Під час розробки ПЗ часто розв'язання проблем продуктивності відкладається на останній момент (коли вже пізно щось вирішувати). Однак є інструменти, що допомагають вчасно виявити слабкі місця у продуктивності та покращити цю ситуацію ще на ранніх етапах розробки.

Зі зростанням застосунку ми починаємо помічати, як страждає наша анімація, а також продуктивність рендерингу компонентів.

Припустимо, що ви займаєтесь розробкою застосунку, де є чат з декількома каналами, до яких можна приєднатися. Зі зростанням кількості повідомлень в кожному каналі (їх стає сотні або навіть тисячі) ми помітимо, як страждає скролінг та перемикання між каналами.

Тож у цій статті ми розглянемо інструменти та методи, які допоможуть виявити та уникнути зайвих рендерів у вашому застосунку на 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 є опція, яка виділяє на екрані елементи під час їх рендерингу (початкового чи повторного).

Оптимізуємо продуктивність React-застосунків

Як можна побачити на прикладі форми, коли ми вводимо дані в поле вводу, отримуємо дуже багато зайвих рендерів. Далі ми розберемося, як можна уникнути такої ситуації.

Оптимізуємо продуктивність React-застосунків

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 не рендерити компонент повторно, тому що результат не зміниться.

Для цього є три методи:

  1. shouldComponentUpdate
  2. React.PureComponent
  3. 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.

Додаткові ресурси

  1. reactjs.org/docs/optimizing-performance.html
  2. github.com/welldone-software/why-did-you-render
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація