Практичні поради: як оптимізувати повільний React-додаток без його переписування
Покроковий посібник з практичних порад: як я оптимізував повільний React-додаток без його переписування: контракти, перевірки та слоти для коду для команд, які використовують цю схему.
Використовуйте цей документ як оновлену версію ідей з статті „Як я оптимізував повільний React-додаток без його переписування“ для співробітників, які працюють з операціями: чіткі етапи, впорядковані блоки коду та примітки щодо відновлення, які залишаються при передачі обов’язків. Етап Огляду найкраще функціонує, якщо його розглядати як вимірювану основу. Запишіть один ідеальний зразок роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань.
Діагностика: виявлення проблеми
Для визначення стадії діагностики необхідно перед зміною коду визначити вхідні дані, відповідального за крок та критерії завершення. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Розміщуйте стан разом із компонентом, який керує змінами. Перенесення всього до глобального сховища ускладнює виявлення проблем із часом виконання.
1. React.memo: Найпростіший спосіб досягнення результату
Для 1 React memo The stage необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись здогадатися про прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Стан слід розміщувати разом із компонентом, який керує змінами. Перенесення всього до глобального сховища ускладнює виявлення проблем з таймінгом.
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
// Component logic
});
2. useMemo та useCallback: Зупинка рекурсивних перерахунків
На етапах useMemo та useCallback необхідно спочатку визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори мають мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Розміщуйте стан разом із компонентом, який керує змінами. Перенесення всього до глобального сховища ускладнює виявлення проблем із таймінгом. На етапах useMemo та useCallback необхідно спочатку визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори мають мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Називайте елементи коду, визначайте критерії успіху та не допускайте безповідомного часткового завершення роботи.
const computedStats = useMemo(() => {
return expensiveAggregation(data);
}, [data]);
const handleUpdate = useCallback((id) => {
updateItem(id);
}, []);
3. useTransition: Пріоритет користувацьких взаємодій
Під час роботи над етапом 3 useTransition «Пріоритет користувацьких взаємодій» спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Відображення витрат на ранньому етапі запобігає несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ. Розглядайте ефекти як синхронізацію з зовнішнім світом, а не як заміну значень, отриманих під час відображення.
const [isPending, startTransition] = useTransition();
const handleSearch = (term) => {
startTransition(() => {
setSearchTerm(term);
// Filtering happens in the background
});
};
4. useDeferredValue: Зменшення витрат на складне відображення
Під час роботи над етапом 4 «Використання DeferredValue для згладжування», спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Розглядайте ефекти як синхронізацію з зовнішнім світом, а не як заміну похідних значень під час відображення.
const deferredData = useDeferredValue(data);
// Chart receives deferredData and updates only when React has idle time
5. Віртуалізація: відображення лише того, що видно
Під час роботи над етапом „Лише віртуалізація та рендеринг“ спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Документуйте як успішний, так і відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Розглядайте наслідки як синхронізацію з зовнішнім світом, а не як заміну отриманих під час рендерингу значень. Під час роботи над етапом „Лише віртуалізація та рендеринг“ спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Назвіть створювані елементи, визначте критерії успіху та не допускайте безповідомних часткових завершень.
import { FixedSizeList } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>
{data[index].name}
</div>
);
<FixedSizeList
height={400}
itemCount={data.length}
itemSize={35}
width="100%"
>
{Row}
</FixedSizeList>
6. Повільне завантаження: розділення коду за маршрутом
Шостий етап повільного завантаження коду працює найкраще, якщо його розглядати як вимірювану характеристику. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на обробку токенів чи запитів разом із функціональними результатами. Візуалізація витрат на ранньому етапі запобігає несподіваним витратам, коли маршрут переходить з демо-середовища у спільні середовища. Зберігайте низькі витрати на обробку даних та відкладайте складну обробку на пізніший час лише після вимірювань. Надмірне використання мемоайзації може приховати помилки у старих даних.
const Dashboard = lazy(() => import('./views/Dashboard'));
const Analytics = lazy(() => import('./views/Analytics'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
7. useMemoizedSelector: оптимізація управління станом
Система керування станом 7 useMemoizedSelector працює найкраще, якщо її розглядати як вимірювану структуру. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу функціоналу. Тримайте конфігурацію поза кодом додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Забезпечуйте легкість обробки даних під час відображення та виконуйте складні обчислення лише після їх вимірювання та за допомогою мемоїзації. Надмірна мемоїзація може приховати проблеми з застарілими параметрами.
import { createSelector } from '@reduxjs/toolkit';
const selectFilteredData = createSelector(
(state) => state.items,
(state) => state.filterTerm,
(items, term) => items.filter(item =>
item.name.toLowerCase().includes(term.toLowerCase())
)
);
// In component:
const filteredData = useSelector(selectFilteredData);
Цифри: до та після
Етап «The Numbers Before» працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний сценарії роботи разом. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Зберігайте витрати на обробку на низькому рівні та відкладайте дорогі операції обчислень на пізніший етап лише після їх вимірювання. Надмірне використання мемоайзації може приховати баги, пов’язані з застарілими даними. Етап «The Numbers Before» працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи.
Зміна мислення
На етапі зміни мислення необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних. Розміщуйте стан разом із компонентом, який керує змінами. Перенесення всього до глобального сховища ускладнює виявлення проблем із часом виконання.
Короткий висновок
На етапі The Bottom Line необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища секретних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Стан слід розміщувати разом із компонентом, який керує змінами. Розміщення всього в глобальному сховищі ускладнює виявлення проблем з таймінгом.
Чек-лист для експлуатації
Під час роботи над етапом чек-листу для експлуатації спочатку необхідно описати умови використання: необхідні вхідні дані, сигнал про успіх та наслідки часткової невдачі. Цей чек-лист допомагає зберігати чесність подальших змін у коді.
Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.
Розглядайте ефекти як синхронізацію з зовнішнім світом, а не як заміну значень, отриманих під час відтворення.
Фіксуйте версії залежностей та записуйте хеш-значення зображення, яке використовувалося під час демонстрації. Відтворюваність краща за „кочові“ знання.
Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Називайте кожен продукт, визначайте критерії успіху та не погоджуйтесь на мовчазне часткове виконання завдань.
Розглядайте ефекти як синхронізацію з зовнішнім світом, а не як заміну значень, отриманих під час відтворення.
Перш ніж запускати стек у продакшн, заморозьте версії, створіть «золотий» запис для критичного шляху та підтвердьте кроки відкату. У спільних середовищах необхідні обмеження швидкості, перевірки прав на використання та чіткий власник для зміни секретів. Віддавайте перевагу надійності перед креативними одноразовими демонстраціями.
Примітка для d6a7f4e83dda: не зберігайте ключі постачальника у репозиторії, встановіть ліміт токенів на сеанс та зберігайте записи поруч із фікстурами для оцінки, щоб подальша заміна моделей залишалася порівнянною.