оптимізація продуктивності

  1. 3.5.1 Використання збірки Production
  2. 3.5.2 Створення програми React
  3. 3.5.3 однофайловий збірки
  4. 3.5.4 Бранч (Brunch)
  5. 3.5.5 Browserify
  6. 3.5.6 Rollup
  7. 3.5.7 webpack
  8. 3.5.8 Профілювання компонентів за допомогою вкладки «Продуктивність Chrome»
  9. 3.5.9 Профілювання компонентів за допомогою профайлера DevTools
  10. 3.5.10 Віртуалізація довгих списків
  11. 3.5.11 Уникнення узгодження
  12. 3.5.12 shouldComponentUpdate в дії
  13. 3.5.13 Приклади
  14. 3.5.14 Міць незмінних даних
  15. 3.5.15 Використання незмінних структур даних

Усередині React використовує кілька розумних підходів, щоб мінімізувати кількість дорогих операцій DOM, необхідних для поновлення користувальницького інтерфейсу. Для багатьох додатків використання React призведе до створення швидкого користувальницького інтерфейсу, не виконуючи багато роботи, щоб спеціально оптимізувати продуктивність. Тим не менш, є кілька способів прискорити ваш додаток React.



3.5.1 Використання збірки Production


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

За замовчуванням React містить багато корисних попереджень. Ці попередження дуже корисні в розробці. Тим не менш, вони роблять React-додаток більше і повільніше, тому ви повинні використовувати production -версію при розгортанні програми.

Якщо ви не впевнені, чи правильно налаштований процес складання, ви можете перевірити це, встановивши React Developer Tools для Chrome. Якщо ви заходите на сайт з React в режимі production, значок буде мати темний фон:

Якщо ви заходите на сайт з React в режимі розробки, значок буде мати червоний фон:

Очікується, що ви використовуєте режим development при роботі з вашим додатком і режим production при розгортанні додатки для користувачів.

Нижче ви можете знайти інструкції по створенню свого застосування для production.


3.5.2 Створення програми React


Якщо ваш проект побудований за допомогою програми Create React , Запустіть:


npm run build

Це створить production-збірка вашого застосування в папці build / вашого проекту.

Пам'ятайте, що це необхідно тільки перед розгортанням в production. Для нормальної розробки використовуйте npm start.


3.5.3 однофайловий збірки


Пропонуються готові версії React і React DOM у вигляді окремих файлів:


<Script src = "https://unpkg.com/ [Email protected] /umd/react.production.min.js "> </ script> <script src =" https://unpkg.com/ [Email protected] /umd/react-dom.production.min.js "> </ script>

Пам'ятайте, що тільки файли React, що закінчуються на .production.min.js, підходять для production.


3.5.4 Бранч (Brunch)


Для найефективнішою production-збірка бранча встановіть плагін uglify-js-brunch:


# Якщо ви використовуєте npm npm install --save-dev uglify-js-brunch # Якщо ви використовуєте Yarn yarn add --dev uglify-js-brunch

Потім, щоб створити production-збірка, додайте прапор -p в команду збірки:


brunch build -p

Пам'ятайте, що вам потрібно зробити це тільки для production збірок. Ви не повинні передавати прапор -p або застосовувати цей плагін в розробці, тому що він приховає корисні попередження React і зробить збірки набагато повільніше.


3.5.5 Browserify


Для найбільш ефективної збірки збірки Browserify встановіть кілька плагінів:


# Якщо ви використовуєте npm npm install --save-dev bundle-collapser envify uglify-js uglifyify # Якщо ви використовуєте Yarn yarn add --dev bundle-collapser envify uglify-js uglifyify

Щоб створити production-збірка, переконайтеся, що ви додали ці перетворення (порядок має значення):

  • Перетворення envify забезпечує правильну середу збірки. Зробіть його глобальним (-g).
  • Перетворення uglifyify усуває обсяги імпорту, додані на стадії розробки. Зробіть його глобальним (-g).
  • Нарешті, результуюча зв'язка передається по каналу в uglify-js для мініфікаціі.

Наприклад:


browserify ./index.js \ -g [envify --NODE_ENV production] \ -g uglifyify \ | uglifyjs --compress --mangle> ./bundle.js

Зверніть увагу! Ім'я пакета - uglifyjs, але двійковий файл, який він надає, називається uglifyjs. Це не помилка.

Пам'ятайте, що вам потрібно зробити це тільки для production збірок. Ви не повинні застосовувати ці плагіни в розробці, тому що вони будуть приховувати корисні попередження React і робити збірки набагато повільніше.



3.5.6 Rollup


Для найбільш ефективної production-збірка Rollup встановіть кілька плагінів:


# Якщо ви використовуєте npm npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-uglify # Якщо ви використовуєте Yarn yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin -uglify

Щоб створити збірку, переконайтеся, що ви додаєте ці плагіни (порядок має значення):

  • Плагін replace забезпечує правильну середу збірки.
  • Плагін commonjs забезпечує підтримку CommonJS в Rollup.
  • Плагін uglify стискає і управляє фінальної зв'язкою (бандлом).

plugins: [// ... require ( 'rollup-plugin-replace') ({ 'process.env.NODE_ENV': JSON.stringify ( 'production')}), require ( 'rollup-plugin-commonjs') ( ), require ( 'rollup-plugin-uglify') (), // ...]

Повний приклад установки дивитися тут .

Пам'ятайте, що вам потрібно зробити це тільки для production збірок. Ви не повинні застосовувати плагін uglify або плагін replace зі значенням «production» в розробці, тому що вони будуть приховувати корисні попередження React і робити збірки набагато повільніше.


3.5.7 webpack


Увага! Якщо ви використовуєте додаток Create React, дотримуйтесь наведених вище інструкцій . Цей розділ має значення, тільки якщо ви конфігуріруете webpack безпосередньо.

Для створення найбільш ефективної webpack production збірки Обов'язково вкажіть ці плагіни в свою production конфігурацію:


new webpack.DefinePlugin ({ 'process.env.NODE_ENV': JSON.stringify ( 'production')}), new webpack.optimize.UglifyJsPlugin ()

Про це ви можете дізнатися в документації по webpack .

Пам'ятайте, що вам потрібно тільки зробити це для production збірок. Ви не повинні застосовувати UglifyJsPlugin або DefinePlugin зі значенням 'production' в розробці, тому що вони будуть приховувати корисні попередження React і робити збірки набагато повільніше.


3.5.8 Профілювання компонентів за допомогою вкладки «Продуктивність Chrome»


У режимі development ви можете візуалізувати, як компоненти монтуються, оновлюються і демонтуються, використовуючи інструменти продуктивності в підтримуваних браузерах. наприклад:

Щоб зробити це в Chrome:

  • Тимчасово вимкніть усі розширення Chrome, особливо React DevTools. Вони можуть значно спотворити результати!
  • Переконайтеся, що ви запускаєте додаток в режимі розробки.
  • Завантажте ваше додаток за допомогою? React_perf в рядку запиту (наприклад, http: // localhost: 3000 /? React_perf).
  • Відкрийте вкладку «Performance» в Chrome DevTools і натисніть «Record».
  • Виконайте дії, які ви хочете профілювати. Чи не записуйте більше 20 секунд або Chrome буде зависати.
  • Зупиніть запис.
  • Події React будуть згруповані під міткою User Timing.

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

В даний час Chrome, Edge і IE є єдиними браузерами, що підтримують цю функцію, але можна використовувати стандартний User Timing API .


3.5.9 Профілювання компонентів за допомогою профайлера DevTools


react-dom 16.5+ і react-native 0.57+ надають розширені можливості профілювання в режимі DEV за допомогою React DevTools профайлера. Огляд профайлера можна знайти в розділі оновлень . Відео-покрокове керівництво по профайлером також доступно на YouTube .

Якщо ви ще не встановили React DevTools, ви можете знайти його тут:


Увага!

Профайлинг продакшен бандла для react-dom також доступний як react-dom / profiling. Детальніше про те, як використовувати цей пакет, читайте на сайті fb.me/react-profiling .

3.5.10 Віртуалізація довгих списків


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

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


3.5.11 Уникнення узгодження


React будує і підтримує внутрішнє уявлення відображуваного призначеного для користувача інтерфейсу. Воно включає елементи React, які ви повертаєте зі своїх компонентів. Це уявлення дозволяє React уникати створення вузлів DOM і доступу до існуючих вузлів без необхідності, оскільки це може бути набагато повільніше, ніж ті ж операції над простими об'єктами JavaScript. Іноді його називають «віртуальним DOM», і воно працює аналогічно в React Native.



Коли властивості або стан компонента змінюються, React вирішує, чи потрібно фактичне оновлення DOM, порівнюючи знову повернутий елемент з раніше відображуваним. Коли вони не рівні, React оновить DOM.

Ви можете візуалізувати ці перемальовування віртуального DOM за допомогою React DevTools:

В консолі розробника виберіть параметр «Highlight Updates» на вкладці «React»:

Взаємодіючи зі своєю сторінкою, ви повинні побачити, що навколо будь-яких компонентів, які були переотрісовани, миттєво з'являються кольорові кордону. Це дозволяє вам виявляти повторні відтворення, що не були необхідними. Ви можете дізнатися більше про функції React DevTools з цього поста в блозі від Ben Edelstein .

Розглянемо цей приклад:

Зверніть увагу, що коли ми впроваджує другу todo, перше todo також блимає на екрані при кожному натисканні клавіші. Це означає, що він повторно отрісовивается React-му разом з елементом input. Це іноді називають «марною / марною» отрисовкой. Тобто ми знаємо, що повторна отрисовка необов'язкова, так як контент першого todo не змінився. Але React про це не знає, через що і виникає такий ефект.

Незважаючи на те, що React оновлює тільки змінені вузли DOM, переотрісовка все ж займає деякий час. У багатьох випадках це не викликає проблем, але якщо уповільнення помітно, ви можете все це прискорити, перевизначивши метод життєвого циклу shouldComponentUpdate (), який запускається до початку процесу повторної відтворення. Реалізація цієї функції за замовчуванням повертає true, вказуючи React виконати оновлення:


shouldComponentUpdate (nextProps, nextState) {return true; }

Якщо ви знаєте, що в деяких ситуаціях ваш компонент не потребує оновлення, ви можете замість цього повернути false з shouldComponentUpdate, щоб пропустити весь процес відтворення, включаючи виклик render () для цього компонента і нижче по ієрархії.

У більшості випадків замість запису shouldComponentUpdate () вручну ви можете успадковуватися від React.PureComponent. Це еквівалентно реалізації shouldComponentUpdate () з неглибоким порівнянням поточних і попередніх props і state.


3.5.12 shouldComponentUpdate в дії


Ось поддерево компонентів. Для кожного з них SCU вказує, що повернув shouldComponentUpdate, а vDOMEq вказує, еквівалентні чи відображаються елементи React. Нарешті, колір кола вказує, чи повинен компонент бути узгоджений чи ні.

Так як shouldComponentUpdate повернув false для поддерева з коренем C2, React не спробувала отрисовать C2, і, отже, навіть не потрібно було викликати shouldComponentUpdate на C4 і C5.

Для C1 і C3 shouldComponentUpdate повернув true, тому React довелося спуститися до листя і перевірити їх. Для C6 shouldComponentUpdate повернув true, і оскільки відображаються елементи не були еквівалентні, React повинен був оновити DOM.

Останній цікавий випадок - C8. React повинен був відобразити цей компонент, але оскільки повертаються їм елементи React були рівні раніше наданими, йому не потрібно було оновлювати DOM.

Зверніть увагу, що React повинен був робити DOM-зміни тільки для C6, що було неминуче. Для C8 цього вдалося уникнути порівнянням отрісовиваємих елементів React, а для піддерев C2 і C7, навіть не довелося порівнювати елементи, так як нас виручив shouldComponentUpdate і отрисовка не викликав.


3.5.13 Приклади


Якщо єдиний спосіб зміни вашого компонента - коли змінна props.style або state.value змінюється, ви могли б виконати перевірку в shouldComponentUpdate як:


class MyCounter extends React.Component {constructor (props) {super (props); this.state = {value: 0}; } ShouldComponentUpdate (nextProps, nextState) {if (this.props.style! == nextProps.style) return true; if (this.state.value! == nextState.value) return true; return false; } Render () {return (<button style = {this.props.style} onClick = {() => this.setState (state => ({value: state.value + 1}))}> Число: {this .state.value} </ button>); }}

У цьому коді shouldComponentUpdate просто перевіряє, чи є які-небудь зміни в props.style або state.value. Якщо ці значення не змінюються, компонент не оновлюється. Якщо ваш компонент став більш складним, ви можете використовувати аналогічну схему «поверхневого порівняння» між усіма полями props і state, щоб визначити, чи повинен компонент оновлюватися.

Цей шаблон настільки поширений, так що React надає помічника для використання даної логіки - просто спадщини від React.PureComponent. Таким чином, наступний код - більш простий спосіб домогтися того ж ефекту:


class MyCounter extends React.PureComponent {constructor (props) {super (props); this.state = {value: 1}; } Render () {return (<button style = {this.props.style} onClick = {() => this.setState (state => ({value: state.value + 1}))}> Число: {this .state.value} </ button>); }}

У більшості випадків ви можете використовувати React.PureComponent замість написання власного shouldComponentUpdate. Він робить тільки неглибоке порівняння, тому ви не можете використовувати його, якщо props або state можуть бути змінені таким чином, що нечітке порівняння буде пропущено.

Це може бути проблемою для більш складних структур даних. Припустимо, що ви хочете, щоб компонент UserList відображав список користувачів, розділених комами, з батьківським компонентом UserAdmin, який дозволяє вам натиснути кнопку, щоб додати чергового користувача в список. Цей код не працює належним чином:


class UserList extends React.PureComponent {render () {return (<h3> {this.props.users.join ( ',')} </ h3>); }} Class UserAdmin extends React.Component {constructor (props) {super (props); this.state = {users: [ 'Користувач 1']}; this.onAddUser = this.onAddUser.bind (this); } OnAddUser () {// Дана секція містить поганий код і призводить до Багам const users = this.state.users; users.push ( `Користувач`); this.setState ({users: users}); } Render () {return (<p> <button onClick = {this.onAddUser} value = "Додати користувача" /> <UserList users = {this.state.users} /> </ p>); }}

Проблема в тому, що PureComponent виконає просте порівняння старих і нових значень this.props.users. Оскільки цей код змінює масив слів в методі onAddUser компонента UserAdmin, старі і нові значення this.props.users при порівнянні будуть однакові, навіть якщо фактичні користувачі в масиві змінилися. Масив, а отже і посилання на нього залишилися тими ж. Таким чином, UserList НЕ буде оновлюватися, навіть якщо він містить нових користувачів, які повинні бути відображені.


3.5.14 Міць незмінних даних


Найпростіший спосіб уникнути цієї проблеми - уникнути зміни значень, які ви використовуєте в якості props або state. Наприклад, описаний вище метод onAddUser можна переписати за допомогою concat ось так:


onAddUser () {this.setState (prevState => ({users: prevState.users.concat ([ `Користувач $ {users.length}`])})); }

ES6 підтримує spread синтаксис для масивів, який може зробити це простіше. Якщо ви використовуєте додаток Create React App , Цей синтаксис доступний за умовчанням.


onAddUser () {this.setState (prevState => ({users: [... prevState.users, `Користувач $ {users.length}`]})); }

Ви також можете переписати код, який змінює об'єкти, щоб уникнути зміни, аналогічним чином. Припустимо, що у нас є об'єкт з ім'ям user, і ми хочемо написати функцію, яка встановлює user.email в передане значення. Ми могли б написати:


function updateUserEmail (user, email) {user.email = email; }

Щоб написати це без зміни вихідного об'єкта, ми можемо використовувати метод Object.assign:


function updateUserEmail (user, email) {return Object.assign ({}, user, {email: email}); }

updateUserEmail тепер повертає новий об'єкт, а не змінює старий. Object.assign входить в ES6 і вимагає Полифила.

JavaScript надає spread оператор для додавання властивостей об'єкта, щоб спростити його оновлення без зміни:


function updateUserEmail (user, email) {return {... user, email: email}; }

Якщо ви використовуєте додаток Create React, за замовчуванням доступні як Object.assign, так і синтаксис spread для об'єктів.


3.5.15 Використання незмінних структур даних


Immutable.js - ще один спосіб вирішити цю проблему. Він надає незмінні, постійні колекції, які працюють через спільне використання структури:

  • Незмінюваність: після створення колекція не може бути змінена в будь-який інший момент часу.
  • Сталість: нові колекції можуть бути створені з попередньої колекції і зміни, такого як set. Оригінальна колекція, як і раніше є дійсною після створення нової колекції.
  • Спільне використання структури: нові колекції створюються з використанням такої ж структури, як і вихідна колекція, що дозволяє скоротити кількість копій до мінімуму для підвищення продуктивності.

Незмінність робить відстеження змін дешевим. Зміна завжди призведе до створення нового об'єкта, тому нам потрібно тільки перевірити, чи змінилася посилання на об'єкт. Наприклад, в цьому звичайному JavaScript-код:


const a = {myProp: 'value1'}; const b = a; b.myProp = 'value2'; a === b; // true

Незважаючи на те, що b був відредагований, оскільки це посилання на той же об'єкт, що і a, це порівняння повертає true. Ви можете написати аналогічний код з immutable.js:


const MyRecord = Immutable.Record ({myProp: null}); const a = new MyRecord ({myProp: 'value1'}); const b = a.set ( 'myProp', 'value2'); a === b; // false

В цьому випадку, оскільки при зміні a повертається нова посилання, ми можемо з упевненістю сказати, що a змінився.

Є дві інші бібліотеки, які можуть допомогти використовувати незмінні дані: seamless-immutable і immutability-helper .

Незмінні структури даних надають вам дешевий спосіб відстеження змін об'єктів, і все, що вам потрібно для реалізації shouldComponentUpdate. Це може дати вам хороший приріст продуктивності.

Завантажте ваше додаток за допомогою?
Наприклад, http: // localhost: 3000 /?