Розділення коду
За замовчуванням, всі залежності проекту об'єднуються в один файл. Чим більше коду, тим повільніше він завантажуватиметься, парситься і виконуватиметься у браузері користувача. На слабких комп'ютерах або телефонах, з поганим підключенням до Інтернет може бути десятки секунд.
При розробці на локальному сервері (localhost
) усі файли раздаються з нашого
комп'ютера. У цьому випадку швидкість підключення до Інтернету не має значення,
та тому файли проекту завантажуються дуже швидко. Однак у продакшені
завантаження великих файлів може стати проблемою, тому що не скрізь є
високошвидкісний інтернет та потужні комп'ютери.
Вирішення проблеми очевидне - розбити проект на дрібніші файли та завантажувати їх лише за потребою. У цьому полягає концепція поділу коду. Якщо користувач заходить на сторінку логіна, не потрібно завантажувати весь код програми, буде достатньо частини, що відповідає за рендер компонентів тільки цієї сторінки.

Поділ коду на кілька файлів це завдання збирача проекту, наприклад Webpack, а не фронтенд фреймворку. Create React App внутрішньо використовує Webpack як збирач і підтримує поділ коду без додаткового налаштування.
Код програми необхідно розділяти за маршрутами та завантажувати за потребою. Цього достатньо для більшості програм. Переходимо на нову сторінку - завантажується необхідний код відображення її компонентів. Такий підхід називається поділ коду на основі маршрутів (route-centric).

Інтерфейси можуть бути дуже громіздкими. Якщо піти далі, то можна оптимізувати завантаження окремих, дуже великих компонентів сторінки, які не потрібні до певної дії користувача. Наприклад, компонент модального вікна, в якому використовується велика бібліотека текстового редактора. Такий підхід називається поділ коду на основі компонентів (component-centric).

Розробник приймає рішення як, що і де поділяти. Проте ось кілька найкращих практик.
- Поділ коду на основі маршрутів є обов'язковим у будь-якому додатку.
- Поділ коду на основі компонентів варто робити тільки у великих, складних інтерфейсах із сотнями компонентів та великими бібліотеками.
- Надмірний поділ коду також не найкраща ідея. HTTP-запит за файлом може бути довше ніж додана вага до першого завантаження.
React.lazy()
та React.Suspense
Ви вже знаєте що ES модулі статичні, тобто імпорти та експорти виконуються у час компіляції, а не під час виконання коду. Імпорти повинні бути оголошені у верхній частині файлу, інакше буде помилка компіляції. Це означає, що ви не можете імпортувати залежність динамічно на основі якоїсь умови.
import MyComponent from "path/to/MyComponent";
const App = () => {
return (
<Routes>
<Route path="/some-path" element={<MyComponent />} />
{/* Інші маршрути */}
</Routes>
);
};
У специфікації ES2020 з'явилася можливість динамічного імпортування модуля.
Різниця в тому, що замість звичайного статичного import
використовується
функція import()
, яка повертає проміс, значенням якого буде файл модуля.
import("path/to/MyComponent").then(module => console.log(module));
React надає API для того, щоб вказати який код необхідно виділити в окремий
файл, а потім завантажувати та рендерувати лише за потреби. Функція
React.lazy()
відповідає за асинхронне завантаження компонента, а Suspense
призупиняє його відображення до завершення завантаження.
import { lazy, Suspense } from "react";
const MyComponent = lazy(() => import("path/to/MyComponent"));
const App = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/some-path" element={<MyComponent />} />
{/* Інші маршрути */}
</Routes>
</Suspense>
);
};
Метод lazy()
очікує функцію-завантажувач, яка повертає результат динамічного
імпорту - проміс, значенням якого буде дефолтний експорт модуль (компонент).
Якщо під час рендеру компонент MyComponent
ще не завантажений, потрібно
показати заглушку. Для цього використовується компонент Suspense
. Проп
fallback
приймає будь-який React-елемент або компонент. Suspense
можна
помістити будь-де над асинхронним компонентом або групою компонентів.
Зверніть увагу на відсутність статичного імпорту MyComponent
в останньому
прикладі. Натомість використовується функція import()
. Якщо залишити статичний
імпорт, то Webpack не виконає поділ коду і додасть весь код MyComponent
в
основний JavaScript файл проекту.
Suspense
та прийом «shared layout»
Якщо ви використовуєте прийом «shared layout», то потрібно розмістити Suspense
безпосередньо всередині компонента SharedLayout
. В іншому випадку, при
завантаженні кожної сторінки, будуть пропадати і повторно рендерувати компоненти
загальної частини сторінок, наприклад хедер та навігація.
// src/components/App.jsx
import { lazy } from "react";
const MyComponent = lazy(() => import("path/to/MyComponent"));
const App = () => {
return (
<Routes>
<Route path="/" element={<SharedLayout />}>
<Route path="some-path" element={<MyComponent />} />
{/* Інші маршрути */}
</Route>
</Routes>
);
};
// src/components/SharedLayout.jsx
import { Suspense } from "react";
import { Outlet } from "react-router-dom";
const SharedLayout = () => {
return (
<Container>
<AppBar>
<Navigation />
<UserMenu />
</AppBar>
<Suspense fallback={<div>Loading...</div>}>
<Outlet />
</Suspense>
</Container>
);
};
Розберіть повний код програми магазину з розділенням коду на основі маршрутів.
Змінився код компонентів
App
,
SharedLayout
та
About
,
а всі компоненти сторінок стали дефолтними експортами.
Зверніть увагу на використання компонента Suspense
у коді компонента сторінки
About
. Так, при завантаженні підсторінок, не заново малюватиметься вся
сторінка, лише її нижня частина з розміткою підсторінок. Компоненти Suspense
у
SharedLayout
та About
не заважають один одному, натомість React використовує
найбільш підходящий - той, що найближче до завантажуваного компоненту.